1
1
graphics/devg/vpoutfb/it66121.c

239 строки
8.1 KiB
C

/*
* (c) 2020, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*************************************************/
/* ITE IT66121 I2C HDMI transmitter interface */
/*************************************************/
/*************************************************/
/* HEADERS */
/*************************************************/
#include "vpoutfb.h"
/*************************************************/
/* I2C FUNCTIONS */
/*************************************************/
static int hdmi_read( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port, uint16_t offset )
{
int ret = 0;
uint8_t data = 0;
ret = i2c_read( vpout, vpout_draw, port, vpout->hdmi[port].device.it66121.bus, vpout->hdmi[port].device.it66121.speed, vpout->hdmi[port].device.it66121.address,
offset, &data, 1 );
if ( ret < 0 )
return ret;
return data;
}
static int hdmi_write_masked( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port, uint8_t offset, uint8_t value, uint8_t mask )
{
int ret = 0;
ret = hdmi_read( vpout, vpout_draw, port, offset );
if ( ret < 0 )
return ret;
value = (value & mask) | ((uint8_t)ret & ~mask);
ret = i2c_write( vpout, vpout_draw, port, vpout->hdmi[port].device.it66121.bus, vpout->hdmi[port].device.it66121.speed, vpout->hdmi[port].device.it66121.address,
offset, &value, 1 );
return (ret < 0) ? ret : 0;
}
/*************************************************/
/* INTERFACE FUNCTIONS */
/*************************************************/
void it66121_probe( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port_index )
{
disp_adapter_t *adapter = vpout->adapter;
uint32_t gpio_data = 0;
uint32_t gpio_direction = 0;
uint32_t gpio_control = 0;
if ( i2c_init( vpout, vpout_draw, vpout->hdmi[port_index].device.it66121.bus, port_index ) )
{
disp_printf( adapter, "[vpoutfb: it66121] Fatal: I2C HDMI controller initialization failed [I2C issue: port=%d]", port_index );
return;
}
/* Initial HDMI chip GPIO port value */
switch ( vpout->hdmi[port_index].device.it66121.reg )
{
case 0xa:
gpio_data = GPIO_SWPORTA_DR;
gpio_direction = GPIO_SWPORTA_DDR;
gpio_control = GPIO_SWPORTA_CTL;
break;
case 0xb:
gpio_data = GPIO_SWPORTB_DR;
gpio_direction = GPIO_SWPORTB_DDR;
gpio_control = GPIO_SWPORTB_CTL;
break;
case 0xc:
gpio_data = GPIO_SWPORTC_DR;
gpio_direction = GPIO_SWPORTC_DDR;
gpio_control = GPIO_SWPORTC_CTL;
break;
case 0xd:
gpio_data = GPIO_SWPORTD_DR;
gpio_direction = GPIO_SWPORTD_DDR;
gpio_control = GPIO_SWPORTD_CTL;
break;
}
/* Switch to the software control */
*GPIO_MMIO32( vpout->hdmi[port_index].device.it66121.registers, gpio_control ) &= ~(1 << vpout->hdmi[port_index].device.it66121.pin);
/* Switch to the output */
*GPIO_MMIO32( vpout->hdmi[port_index].device.it66121.registers, gpio_direction ) |= 1 << vpout->hdmi[port_index].device.it66121.pin;
*GPIO_MMIO32( vpout->hdmi[port_index].device.it66121.registers, gpio_data ) |= 1 << vpout->hdmi[port_index].device.it66121.pin;
}
void it66121_reset( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port_index )
{
uint32_t gpio_data = 0;
switch ( vpout->hdmi[port_index].device.it66121.reg )
{
case 0xa:
gpio_data = GPIO_SWPORTA_DR;
break;
case 0xb:
gpio_data = GPIO_SWPORTB_DR;
break;
case 0xc:
gpio_data = GPIO_SWPORTC_DR;
break;
case 0xd:
gpio_data = GPIO_SWPORTD_DR;
break;
}
*GPIO_MMIO32( vpout->hdmi[port_index].device.it66121.registers, gpio_data ) &= ~(1 << vpout->hdmi[port_index].device.it66121.pin);
/* Takes a pause or reset won't take effect */
disp_usecspin( 1000 );
*GPIO_MMIO32( vpout->hdmi[port_index].device.it66121.registers, gpio_data ) |= 1 << vpout->hdmi[port_index].device.it66121.pin;
}
void it66121_remove( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port_index )
{
i2c_fini( vpout, vpout_draw, port_index );
}
int it66121_init( vpout_context_t *vpout, vpout_draw_context_t *vpout_draw, uint8_t port_index /* ignored */, uint32_t pixel_clock )
{
disp_adapter_t *adapter = vpout->adapter;
uint8_t ident[] = { 0x54, 0x49, 0x12, 0x16 };
int ret = 0, i;
it66121_reset( vpout, vpout_draw, port_index );
disp_usecspin( 5000 );
/* Verify it's the correct device */
for ( i = 0; i < 4; i++ )
{
if ( ident[i] != hdmi_read( vpout, vpout_draw, port_index, i ) )
{
disp_printf( adapter, "[vpoutfb: it66121] Fatal: ITE IT66121 I2C HDMI controller not found [port=%d]", port_index );
return (-ENODEV);
}
}
/* reset whole circuit */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_RESET, 0x20, 0x20 );
disp_usecspin( 5000 );
/* each circuit is off so we can switch them on one by one */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_RESET, RESET_RCLK_MASK | RESET_AUDIO_MASK | RESET_VIDEO_MASK | RESET_AFIFO_MASK |
RESET_HDCP_MASK, 0xFF );
disp_usecspin( 5000 );
/* hdmi tx flipflops reset */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_TX_RESET, 0x10, 0xFF );
/* DVI mode, packet interface off */
for ( i = 0xc0; i <= 0xd0; i++ )
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, i, 0, 0xFF );
/* Ignore all the interrupts */
for ( i = REG_IRQ_FIRST; i <= REG_IRQ_LAST; i++ )
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, i, 0xFF, 0xFF );
/* Enable video circuit */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_RESET, 0x00, RESET_VIDEO_MASK );
/* Switch avmute on */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_AVMUTE, 0x01, 0xFF );
/* Disable audio */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, 0xE0, 0x00, 0x0F );
/* Set up clock-related settings */
if ( (float)pixel_clock >= (float)12500.0 )
{
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK1, 0x18, 0xFF );
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK2, 0x10, 0xFF );
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK3, 0x0C, 0xFF );
} else {
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK1, 0x88, 0xFF );
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK2, 0x10, 0xFF );
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_CLK3, 0x84, 0xFF );
}
/* Clear TX FIFO */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_TXFIFO_SET, 0x02, 0x02 );
disp_usecspin( 2000 );
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_TXFIFO_SET, 0x00, 0x02 );
/* It takes a while to measure clocks, so we give it time */
disp_usecspin( 150000 );
for ( i = 0; i < 10; i++ )
{
if ( !(hdmi_read( vpout, vpout_draw, port_index, 0x0e ) & 0x10) )
{
disp_printf( adapter, "[vpoutfb: it66121] Warning: transmitter video input not stable [port=%d]", port_index );
disp_usecspin( 20000 );
} else
break;
}
if ( i == 10 )
{
it66121_reset( vpout, vpout_draw, port_index );
return (-EBUSY);
}
/* Switch avmute off */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, REG_AVMUTE, 0x00, 0xFF );
/* Turn on display */
ret |= hdmi_write_masked( vpout, vpout_draw, port_index, 0x61, 0x03, 0xFF );
if ( ret )
it66121_reset( vpout, vpout_draw, port_index );
return ret;
}