239 строки
8.1 KiB
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;
|
||
|
}
|