From 9a587d18417c401acd4a668f3495db6fe50738ba Mon Sep 17 00:00:00 2001 From: ckkim Date: Tue, 15 Feb 2022 16:49:00 +0900 Subject: ODROID-C4:support splash screen on odroid-vu7c Signed-off-by: ckkim Change-Id: I837dc0ff6a0259fde6a042822275d9b9c886347a --- board/hardkernel/odroid-common/Makefile | 1 + board/hardkernel/odroid-common/lt8619c.c | 948 +++++++++++++++++++++++++ board/hardkernel/odroid-common/lt8619c.h | 114 +++ board/hardkernel/odroid-common/odroid-common.h | 1 + board/hardkernel/odroid-common/usb.c | 12 +- board/hardkernel/odroidc4/odroidc4.c | 7 + 6 files changed, 1081 insertions(+), 2 deletions(-) create mode 100755 board/hardkernel/odroid-common/lt8619c.c create mode 100644 board/hardkernel/odroid-common/lt8619c.h diff --git a/board/hardkernel/odroid-common/Makefile b/board/hardkernel/odroid-common/Makefile index 03f3bb9ba5..64adfe1b0a 100644 --- a/board/hardkernel/odroid-common/Makefile +++ b/board/hardkernel/odroid-common/Makefile @@ -1,2 +1,3 @@ obj-y += board.o partition.o eth_setup.o recovery.o adc.o usb.o obj-$(CONFIG_AML_CVBS) += cvbs.o +obj-$(CONFIG_SYS_I2C_AML) += lt8619c.o diff --git a/board/hardkernel/odroid-common/lt8619c.c b/board/hardkernel/odroid-common/lt8619c.c new file mode 100755 index 0000000000..8e742c7997 --- /dev/null +++ b/board/hardkernel/odroid-common/lt8619c.c @@ -0,0 +1,948 @@ +/* + * LT8619C HDMI to RGB888 converter + * + * Copyright (C) 2020 Hardkernel Co., Ltd. + * + * Author : CKKim + * + * The base initial code of this driver is from Lotium semiconductor + * and output signal of LT8619C is fixed as following. + * - resolution 1024x600 + * - 1 port RGB888 + * - 8bit, DE timing mode + * + * FIXME for copyright notification and driver license level + */ +#include +#include +#include + +#include +#include +#include +#include "lt8619c.h" + +static struct _LT8619C_RXStatus RXStat, *pRXStat; + +static uint16_t h_active, v_active; +static uint16_t h_syncwidth, v_syncwidth; +static uint16_t h_bkporch, v_bkporch; +static uint16_t h_total, v_total; +static uint8_t h_syncpol, v_syncpol; +static uint32_t frame_counter; + +/* onchip EDID */ +uint8_t onchip_edid[256]={ + /* 1024*768 */ + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x21, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x1b, 0x01, 0x03, 0x80, 0x23, 0x1a, 0x78, + 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, + 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19, + 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90, + 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, + 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, + 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x48, 0x4b, 0x5f, 0x56, 0x55, 0x38, 0x43, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x6a, + + 0x02, 0x03, 0x12, 0xf1, 0x23, 0x09, 0x07, 0x07, + 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6 +}; + +/* lcd timing */ +struct video_timing lcd_timing = { + /* pix_clk/kHz, hfp, hs, hbp,hact,htotal,vfp, vs, vbp,vact, vtotal */ + /* 1024x600p60 */ + 50400, 24, 136, 160, 1024, 1344, 3, 6, 29, 600, 638 +}; + +static uint8_t lt8619c_reg_read(char reg) +{ + uint8_t out_buf[2]; + uint8_t in_buf[2]; + uint8_t val = 0; + + struct i2c_msg msgs[] = { + { + .addr = LT8619C_ADDR, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = LT8619C_ADDR, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = reg; + out_buf[1] = 0; + + if (aml_i2c_xfer(msgs, 2) == 2) + val = in_buf[0]; + else + printf("i2c 0x%02x read failed\n", reg); + + return val; +} + +static int lt8619c_reg_write(uint8_t reg, uint8_t data) +{ + uint8_t outbuf[2]; + struct i2c_msg msg = { + .addr = LT8619C_ADDR, + .flags = 0, + .len = 2, + .buf = outbuf, + }; + + outbuf[0] = reg; + outbuf[1] = data; + + if (aml_i2c_xfer(&msg, 1) != 1) + printf("i2c 0x%02x 0x%02x write failed\n", reg, data); + + return 0; +} + +static int lt8619c_reg_write_bytes(uint8_t reg, uint8_t *data, uint16_t length) +{ + uint8_t outbuf[length + 1]; + struct i2c_msg msg = { + .addr = LT8619C_ADDR, + .flags = 0, + .len = length + 1, + .buf = outbuf, + }; + + outbuf[0] = reg; + memcpy(outbuf + 1, data, length); + + if (aml_i2c_xfer(&msg, 1) != 1) + printf("i2c write failed\n"); + + return 0; +} + +static bool lt8619c_check_chipid(void) +{ + unsigned int chip_id[3]; + + lt8619c_reg_write(0xff, 0x60); + + chip_id[0] = lt8619c_reg_read(0x00); + chip_id[1] = lt8619c_reg_read(0x01); + chip_id[2] = lt8619c_reg_read(0x02); + printf("Read Chip : 0x%x, 0x%x, 0x%x\n", + chip_id[0], chip_id[1], chip_id[2]); + + if ((chip_id[0] == 0x16) && chip_id[1] == 0x04) + return true; + else + return false; +} + +static void lt8619c_setHPD(uint8_t level) +{ + lt8619c_reg_write(0xFF, 0x80); + + if (level) + lt8619c_reg_write(0x06, (lt8619c_reg_read(0x06)|0x08)); + else + lt8619c_reg_write(0x06, (lt8619c_reg_read(0x06)&0xF7)); +} + +static void lt8619c_edid_calc(uint8_t *pbuf, struct video_timing *timing) +{ + uint16_t hblanking = 0; + uint16_t vblanking = 0; + uint16_t pixel_clk; + + pixel_clk = timing->pixel_clk / 10; + + if (pbuf == NULL || timing == NULL) + return; + + hblanking = timing->hfp + timing->hs + timing->hbp; /* H blanking */ + vblanking = timing->vfp + timing->vs + timing->vbp; /* V blanking */ + + pbuf[0] = pixel_clk % 256; + pbuf[1] = pixel_clk / 256; + pbuf[2] = timing->hact % 256; + pbuf[3] = hblanking % 256; + pbuf[4] = ((timing->hact / 256) <<4) + hblanking / 256; + pbuf[5] = timing->vact % 256; + pbuf[6] = vblanking % 256; + pbuf[7] = ((timing->vact / 256) << 4) + vblanking / 256; + pbuf[8] = timing->hfp % 256; + pbuf[9] = timing->hs % 256; + pbuf[10] = ((timing->vfp % 256) << 4) + timing->vs % 256; + pbuf[11] = ((timing->hfp/256) << 6) + ((timing->hs / 256) << 4)+ + ((timing->vfp / 256) << 2) + (timing->vs / 256); + pbuf[17] = 0x1c; /* progress negative vsync, negative hsync */ +} + +static uint8_t lt8619c_edid_checksum(uint8_t block, uint8_t *buf) +{ + uint8_t i = 0; + uint8_t checksum = 0; + uint8_t *pbuf = buf + 128 * block; + + if (pbuf == NULL || (pbuf + 127) == NULL) + return 0; + + for (i = 0, checksum = 0; i < 127 ; i++) { + checksum += pbuf[i]; + checksum %= 256; + } + checksum = 256 - checksum; + + printf("EDID checksum 0x%x\n", checksum); + + return checksum; +} + +static void lt8619c_set_edid(uint8_t * edid_buf) +{ + int ret = 0; + + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x8E, 0x07); + lt8619c_reg_write(0x8F, 0x00); + + if(edid_buf == NULL) + ret = lt8619c_reg_write_bytes(0x90, &onchip_edid[0], 256); + else + ret = lt8619c_reg_write_bytes(0x90, edid_buf, 256); + + if (ret) + printf("EDID Set write error\n"); + + lt8619c_reg_write(0x8E, 0x02); +} + +static void lt8619c_rx_init(void) +{ + lt8619c_reg_write(0xFF, 0x80); + /* RGD_CLK_STABLE_OPT[1:0] */ + lt8619c_reg_write(0x2c, (lt8619c_reg_read(0x2C)|0x30)); + + lt8619c_reg_write(0xFF, 0x60); + lt8619c_reg_write(0x04, 0xF2); + lt8619c_reg_write(0x83, 0x3F); + lt8619c_reg_write(0x80, 0x08); /* use xtal_clk as sys_clk */ +#ifdef DDR_CLK + lt8619c_reg_write(0xa4, 0x14); /* 0x10:SDR clk,0x14: DDR clk */ +#else + lt8619c_reg_write(0xa4, 0x10); /* 0x10:SDR clk,0x14: DDR clk */ +#endif + + printf("LT8619C output mode : %d\n", LT8619C_OUTPUTMODE); + + printf("LT8619C set to OUTPUT_RGB888\n"); + lt8619c_reg_write(0xFF, 0x60); + lt8619c_reg_write(0x07, 0xff); + lt8619c_reg_write(0xa8, 0x0f); + lt8619c_reg_write(0x60, 0x00); + lt8619c_reg_write(0x96, 0x71); + lt8619c_reg_write(0xa0, 0x50); + // 0x60A3=0x44:Phase adjust enable;0x60A3=0x30:PIN68 + // switch to output PCLK(only use for U5); + lt8619c_reg_write(0xa3, 0x74); + //Phase code value: 0x20,0x28,0x21,0x29,0x22,0x2a,0x23,0x2b,0x24,0x2c + lt8619c_reg_write(0xa2, 0x29); + //RGB mapping control + lt8619c_reg_write(0x6d, 0x00);//0x07//00 + //RGB high/low bit swap control + lt8619c_reg_write(0x6e, 0x00); + /* LT8619C_OUTPUTMODE == OUTPUT_RGB888 */ + + lt8619c_reg_write(0xff, 0x60); + lt8619c_reg_write(0x0e, 0xfd); + lt8619c_reg_write(0x0e, 0xff); + lt8619c_reg_write(0x0d, 0xfc); + lt8619c_reg_write(0x0d, 0xff); +} + +void lt8619c_audio_init(uint8_t audio_input) +{ + if (audio_input == I2S_2CH) { + printf("Audio Init : Audio set to I2S_2CH\n"); + lt8619c_reg_write(0xff, 0x60); + lt8619c_reg_write(0x4c, 0x00); + } else if (audio_input == SPDIF) { + printf("Audio Init : Audio set to SPDIF\n"); + lt8619c_reg_write(0xff, 0x60); + lt8619c_reg_write(0x4c, 0x80); + } else { + printf("Audio Init : Error! Check Audio Mode\n"); + } + + lt8619c_reg_write(0xff, 0x80); + lt8619c_reg_write(0x5d, 0xc9); + lt8619c_reg_write(0x07, 0x16); + lt8619c_reg_write(0x08, 0x80); +} + +static void lt8619c_poweron(void) +{ + /* set HPD as low */ + lt8619c_setHPD(0); + + /* calculate timing & set edid */ + lt8619c_edid_calc(onchip_edid + 0x36, &lcd_timing); + onchip_edid[127] = lt8619c_edid_checksum(0, &onchip_edid[0]); + lt8619c_set_edid(onchip_edid); + + mdelay(100); + + /* set HPD as low */ + lt8619c_setHPD(1); + + /* rx init */ + lt8619c_rx_init(); + + /* audio init */ + lt8619c_audio_init(I2S_2CH); + + mdelay(1); +} + +static void lt8619c_rx_reset(void) +{ + lt8619c_reg_write(0xFF, 0x60); + lt8619c_reg_write(0x0E, 0xBF); /* reset RXPLL */ + lt8619c_reg_write(0x09, 0xFD); /* reset RXPLL Lock det */ + mdelay(5); + + lt8619c_reg_write(0x0E, 0xFF); /* release RXPLL */ + lt8619c_reg_write(0x09, 0xFF); + + lt8619c_reg_write(0xFF, 0x60); + lt8619c_reg_write(0x0e, 0xC7); /* reset PI */ + lt8619c_reg_write(0x09, 0x0F); /* reset RX,CDR */ + mdelay(10); + + lt8619c_reg_write(0x0e, 0xFF); /* release PI */ + mdelay(10); + lt8619c_reg_write(0x09, 0x8F); /* release RX */ + mdelay(10); + lt8619c_reg_write(0x09, 0xFF); /* release CDR */ + mdelay(50); +} + +static bool lt8619c_detect_clk(void) +{ + uint8_t read_data; + bool ret; + + ret = true; + lt8619c_reg_write(0xFF, 0x80); + if (lt8619c_reg_read(0x44) & 0x08) { + if (!pRXStat->flag_RXClkStable) { + pRXStat->flag_RXClkStable = + !pRXStat->flag_RXClkStable; + printf("[detect_clk] rx clock stable %d\n", + pRXStat->flag_RXClkStable); + lt8619c_reg_write(0xFF, 0x60); + read_data = lt8619c_reg_read(0x97); + lt8619c_reg_write(0x97, (read_data & 0x3f)); + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x1b, 0x00); + + lt8619c_rx_reset(); + mdelay(5); + + lt8619c_reg_write(0xFF, 0x80); + read_data = (lt8619c_reg_read(0x87) & 0x10); + if (read_data) { + pRXStat->flag_RXPLLLocked = true; + printf("[detect_clk] clock detected & pll lock ok\n"); + ret = true; + } else { + pRXStat->flag_RXPLLLocked = false; + memset(pRXStat, 0, sizeof(RXStat)); + printf("[detect_clk] clock detected & pll unlock\n"); + ret = false; + } + } else { + lt8619c_reg_write(0xFF, 0x80); + read_data = lt8619c_reg_read(0x87) & 0x10; + + if (read_data) { + pRXStat->flag_RXPLLLocked = true; + printf("[detect_clk] pll lock ok\n"); + ret = true; + } else { + pRXStat->flag_RXPLLLocked = false; + memset(pRXStat, 0, sizeof(RXStat)); + printf("[detect_clk] pll unlock\n"); + ret = false; + } + } + } else { + if (pRXStat->flag_RXClkStable) + printf("[detect_clk] lt8619c clock disappeared\n"); + memset(pRXStat, 0, sizeof(RXStat)); + ret = false; + } + return ret; +} + +static void lt8619c_input_info(void) +{ + uint8_t loop_num,read_data; + + lt8619c_reg_write(0xFF,0x80); + + if (pRXStat->flag_RXClkStable && pRXStat->flag_RXPLLLocked) { + printf("[input info] check hsync start: \n"); + + if (lt8619c_reg_read(0x13) & 0x01) { + if (!pRXStat->Flag_HsyncStable) { + pRXStat->Flag_HsyncStable = true; + for (loop_num = 0; loop_num < 8; loop_num++) { + printf("[input info] check hsync : %d\n", + loop_num); + mdelay(20); + + if(!(lt8619c_reg_read(0x13) & 0x01)) { + printf("[input info] lt8619c 8013[0]=0\n"); + pRXStat->Flag_HsyncStable = false; + printf("[input info] hsync stable FAIL\n"); + break; + } + } + + if (pRXStat->Flag_HsyncStable) { + lt8619c_reg_write(0xFF,0x60); + read_data = lt8619c_reg_read(0x0D); + /* reset LVDS/BT fifo */ + lt8619c_reg_write(0x0D, + read_data & 0xf8); + lt8619c_reg_write(0x0D, + read_data | 0x06); + lt8619c_reg_write(0x0D, + read_data | 0x01); + printf("[input info] hsync stable ok\n"); + } + } + } else { + if (pRXStat->Flag_HsyncStable) + printf("[input info] hsync stable -> unstable\n"); + + pRXStat->Flag_HsyncStable = false; + printf("[input info] hsync keep unstable\n"); + //memset(pRXStat, 0, sizeof(_RXStat)); + } + } + + if (pRXStat->Flag_HsyncStable) { + read_data = lt8619c_reg_read(0x13); + pRXStat->input_hdmimode = + (read_data & 0x02)?(true):(false); + if (pRXStat->input_hdmimode) { + pRXStat->input_vic = lt8619c_reg_read(0x74) & 0x7f; + pRXStat->input_colorspace = + lt8619c_reg_read(0x71) & 0x60; + pRXStat->input_colordepth = + lt8619c_reg_read(0x16) & 0xf0; + pRXStat->input_colorimetry = + lt8619c_reg_read(0x72) & 0xc0; + pRXStat->input_ex_colorimetry = + lt8619c_reg_read(0x73) & 0x70; + pRXStat->input_QuantRange = + lt8619c_reg_read(0x73) & 0x0c; + pRXStat->input_PRfactor = + lt8619c_reg_read(0x75) & 0x0f; + + if (pRXStat->input_PRfactor == 1) { + lt8619c_reg_write(0xFF, 0x60); + read_data = lt8619c_reg_read(0x97); + lt8619c_reg_write(0x97, read_data | 0x40); + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x1b, 0x20); + } else if (pRXStat->input_PRfactor == 3) { + lt8619c_reg_write(0xFF, 0x60); + read_data = lt8619c_reg_read(0x97); + lt8619c_reg_write(0x97, read_data | 0x80); + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x1b, 0x60); + } else { + lt8619c_reg_write(0xFF, 0x60); + read_data = lt8619c_reg_read(0x97); + lt8619c_reg_write(0x97, read_data & 0x3f); + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x1b, 0x00); + } + } else { + pRXStat->input_vic = 0; + pRXStat->input_colorspace = COLOR_RGB; + pRXStat->input_colordepth = 0; + pRXStat->input_colorimetry = ITU_709; + pRXStat->input_ex_colorimetry = 0; + pRXStat->input_QuantRange = FULL_RANGE; + pRXStat->input_PRfactor = 0; + lt8619c_reg_write(0xFF, 0x60); + read_data = lt8619c_reg_read(0x97); + lt8619c_reg_write(0x97, read_data & 0x3f); + lt8619c_reg_write(0xFF, 0x80); + lt8619c_reg_write(0x1b, 0x00); + } + } +} + +static void lt8619c_csc_conversion(void) +{ + printf("[csc conv] output color 0x%x\n", + LT8619C_OUTPUTCOLOR); + + lt8619c_reg_write(0xFF, 0x60); + lt8619c_reg_write(0x07, 0xfe); + + if (LT8619C_OUTPUTCOLOR == COLOR_RGB) { + if (pRXStat->input_colorspace == COLOR_RGB) { + lt8619c_reg_write(0x52, 0x00); + if (pRXStat->input_QuantRange == LIMIT_RANGE) + lt8619c_reg_write(0x53, 0x08); + else + lt8619c_reg_write(0x53, 0x00); + } else { + if (pRXStat->input_colorspace == COLOR_YCBCR422) + lt8619c_reg_write(0x52, 0x01); + else + lt8619c_reg_write(0x52, 0x00); + + if (pRXStat->input_QuantRange == LIMIT_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x53, 0x50); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x53, 0x70); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x53, 0x70); + } else if (pRXStat->input_QuantRange == FULL_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x53, 0x40); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x53, 0x60); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x53, 0x60); + } else { /* DEFAULT_RANGE or RESERVED_VAL */ + lt8619c_reg_write(0x53, 0x60); + } + } + } else if (LT8619C_OUTPUTCOLOR == COLOR_YCBCR444) { + if (pRXStat->input_colorspace == COLOR_RGB) { + lt8619c_reg_write(0x53, 0x00); + if (pRXStat->input_QuantRange == LIMIT_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x52, 0x08); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x52, 0x28); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x52, 0x28); + } else if (pRXStat->input_QuantRange == FULL_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x52, 0x18); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x52, 0x38); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x52, 0x38); + } else { /* DEFAULT_RANGE or RESERVED_VAL */ + lt8619c_reg_write(0x52, 0x38); + } + } else if (pRXStat->input_colorspace == COLOR_YCBCR444) { + lt8619c_reg_write(0x52, 0x00); + lt8619c_reg_write(0x53, 0x00); + } else if (pRXStat->input_colorspace == COLOR_YCBCR422) { + lt8619c_reg_write(0x52, 0x01); + lt8619c_reg_write(0x53, 0x00); + } + } else if (LT8619C_OUTPUTCOLOR == COLOR_YCBCR422) { + if (pRXStat->input_colorspace == COLOR_RGB) { + lt8619c_reg_write(0x53, 0x00); + + if (pRXStat->input_QuantRange == LIMIT_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x52, 0x0a); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x52, 0x2a); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x52, 0x2a); + } else if (pRXStat->input_QuantRange == FULL_RANGE) { + if (pRXStat->input_colorimetry == ITU_601) + lt8619c_reg_write(0x52, 0x1a); + else if (pRXStat->input_colorimetry == ITU_709) + lt8619c_reg_write(0x52, 0x3a); + else /* NO_DATA or EXTENDED_COLORIETRY */ + lt8619c_reg_write(0x52, 0x3a); + } else { /* DEFAULT_RANGE or RESERVED_VAL */ + lt8619c_reg_write(0x52, 0x3a); + } + } else if (pRXStat->input_colorspace == COLOR_YCBCR444) { + lt8619c_reg_write(0x52, 0x02); + lt8619c_reg_write(0x53, 0x00); + } else if (pRXStat->input_colorspace == COLOR_YCBCR422) { + lt8619c_reg_write(0x52, 0x00); + lt8619c_reg_write(0x53, 0x00); + } + } +} + +static void lt8619c_check_video(void) +{ + uint8_t tmp_read; + + if (!pRXStat->Flag_HsyncStable) { + h_total = 0; + v_total = 0; + return; + } + + lt8619c_reg_write(0xFF, 0x60); + + h_active = ((uint16_t)lt8619c_reg_read(0x22)) << 8; + h_active += lt8619c_reg_read(0x23); + v_active = ((uint16_t)(lt8619c_reg_read(0x20) & 0x0f)) << 8; + v_active += lt8619c_reg_read(0x21); + + frame_counter = ((uint32_t)lt8619c_reg_read(0x10)) << 16; + frame_counter += ((uint32_t)lt8619c_reg_read(0x11)) << 8; + frame_counter += lt8619c_reg_read(0x12); + + h_syncwidth = ((uint16_t)(lt8619c_reg_read(0x14) & 0x0f)) << 8; + h_syncwidth += lt8619c_reg_read(0x15); + v_syncwidth = lt8619c_reg_read(0x13); + + h_bkporch = ((uint16_t)(lt8619c_reg_read(0x18) & 0x0f)) << 8; + h_bkporch += lt8619c_reg_read(0x19); + v_bkporch = lt8619c_reg_read(0x16); + + h_total = ((uint16_t)lt8619c_reg_read(0x1e)) << 8; + h_total += lt8619c_reg_read(0x1f); + v_total = ((uint16_t)(lt8619c_reg_read(0x1c) & 0x0f)) << 8; + v_total += lt8619c_reg_read(0x1d); + + tmp_read = lt8619c_reg_read(0x24); + h_syncpol = tmp_read & 0x01; + v_syncpol = (tmp_read & 0x02) >> 1; +} + +static void detect_lvdspll_lock(void) +{ + uint8_t read_data_1; + uint8_t check_num = 0; + + lt8619c_reg_write(0xFF, 0x60); + + if ((lt8619c_reg_read(0xA3) & 0x40) == 0x40) { + lt8619c_reg_write(0xFF, 0x80); + while ((lt8619c_reg_read(0x87) & 0x20) == 0x00) { + lt8619c_reg_write(0xFF, 0x60); + read_data_1 = lt8619c_reg_read(0x0e); + lt8619c_reg_write(0x0e, read_data_1 & 0xFD); + + mdelay(5); + + lt8619c_reg_write(0x0e, 0xFF); + lt8619c_reg_write(0xFF, 0x80); + + check_num++; + if(check_num > 10) + break; + } + } +} + +static void lt8619c_bt_setting(void) +{ + uint8_t val_6060; + uint16_t tmp_data; + + if (!pRXStat->Flag_HsyncStable) + return; + + detect_lvdspll_lock(); + + lt8619c_reg_write(0xFF, 0x60); + val_6060 = lt8619c_reg_read(0x60) & 0xc7; + + /* set BT TX h/vsync polarity */ + if (h_syncpol) + val_6060 |= 0x20; + + if (v_syncpol) + val_6060 |= 0x10; + + /* + * double the value of v_active&v_total when input is interlace resolution. + * if user needs to support interlace format not listed here, + * please add that interlace format info here. + */ + if (pRXStat->input_hdmimode) { + switch (pRXStat->input_vic) { + case 5: /* 1080i */ + case 6: /* 480i */ + case 7: /* 480iH */ + case 10: /* 480i4x */ + case 11: /* 480i4xH */ + case 20: /* 1080i25 */ + printf("[bt setting] VIC20\n"); + val_6060 |= 0x08; + v_active <<= 1; + + if (v_total % 2 == 1) + v_total = (v_total << 1) - 1; + else + v_total = (v_total << 1) + 1; + + lt8619c_reg_write(0x68, 23); + break; + case 21: /* 576i */ + case 22: /* 576iH */ + case 25: /* 576i4x */ + case 26: /* 576i4xH */ + printf("[bt setting] VIC26\n"); + val_6060 |= 0x08; + v_active <<= 1; + + if (v_total % 2 == 1) + v_total = (v_total << 1) - 1; + else + v_total = (v_total << 1) + 1; + + lt8619c_reg_write(0x68, 25); + break; + default: + lt8619c_reg_write(0x68, 0x00); + break; + } + /* DVI Input */ + } else { + if ((h_active == 1920) && (v_active == 540)) { + val_6060 |= 0x08; + v_active <<= 1; + if (v_total % 2 == 1) + v_total = (v_total << 1) - 1; + else + v_total = (v_total << 1) + 1; + lt8619c_reg_write(0x68, 23); + } else if ((h_active == 1440) && (v_active == 240)) { + val_6060 |= 0x08; + v_active <<= 1; + if (v_total % 2 == 1) + v_total = (v_total << 1) - 1; + else + v_total = (v_total << 1) + 1; + lt8619c_reg_write(0x68, 23); + } else if ((h_active == 1440) && (v_active == 288)) { + val_6060 |= 0x08; + v_active <<= 1; + if (v_total % 2 == 1) + v_total = (v_total << 1) - 1; + else + v_total = (v_total << 1) + 1; + lt8619c_reg_write(0x68, 25); + } + } + + lt8619c_reg_write(0x60, val_6060); + tmp_data = h_syncwidth + h_bkporch; + lt8619c_reg_write(0x61, (uint8_t)(tmp_data >> 8)); + lt8619c_reg_write(0x62, (uint8_t)tmp_data); + tmp_data = h_active; + lt8619c_reg_write(0x63, (uint8_t)(tmp_data >> 8)); + lt8619c_reg_write(0x64, (uint8_t)tmp_data); + tmp_data = h_total; + lt8619c_reg_write(0x65, (uint8_t)(tmp_data >> 8)); + lt8619c_reg_write(0x66, (uint8_t)tmp_data); + tmp_data = v_syncwidth + v_bkporch; + lt8619c_reg_write(0x67, (uint8_t)tmp_data); + tmp_data = v_active; + lt8619c_reg_write(0x69, (uint8_t)(tmp_data >> 8)); + lt8619c_reg_write(0x6a, (uint8_t)tmp_data); + tmp_data = v_total; + lt8619c_reg_write(0x6b, (uint8_t)(tmp_data >> 8)); + lt8619c_reg_write(0x6c, (uint8_t)tmp_data); +} + +static void lt8619c_print_rxinfo(void) +{ + uint32_t clk=0; + static uint16_t print_en; + uint32_t audio_rate = 0; + + print_en = 0; + + if (!pRXStat->Flag_HsyncStable) { + print_en = 0; + return; + } + if (print_en != 3) + print_en++; + + if (print_en == 3) { + print_en = 0; + if (pRXStat->input_hdmimode) { + printf("[rxinfo] input is HDMI signal\n"); + printf("[rxinfo] input vid %d\n", + pRXStat->input_vic); + + if (pRXStat->input_colorspace == COLOR_RGB) + printf( + "[rxinfo] input colorspace : COLOR_RGB\n"); + else if (pRXStat->input_colorspace == COLOR_YCBCR444) + printf("[rxinfo] input colorspace : COLOR_YCBCR444\n"); + else if (pRXStat->input_colorspace == COLOR_YCBCR422) + printf("[rxinfo] input colorspace : COLOR_YCBCR422\n"); + else + printf("[rxinfo] input colorspace UNKNOWN\n"); + + printf("[rxinfo] input colordepth %d\n", + pRXStat->input_colordepth); + + if (pRXStat->input_colorimetry == NO_DATA) + printf("[rxinfo] input colorimetry : NO DATA\n"); + else if (pRXStat->input_colorimetry == ITU_601) + printf("[rxinfo] input colorimetry : ITU 601\n"); + else if (pRXStat->input_colorimetry == ITU_709) + printf("[rxinfo] input colorimetry : ITU 709\n"); + else + printf("[rxinfo] input colorimetry : extended colorimetry\n"); + + if (pRXStat->input_colorimetry == EXTENDED_COLORIETRY) { + if (pRXStat->input_ex_colorimetry == xvYCC601) + printf("[rxinfo] input ex colorimetry : xvYCC601\n"); + else if + (pRXStat->input_ex_colorimetry == + xvYCC709) + printf("[rxinfo] input ex colorimetry : xvYCC709\n"); + else + printf("[rxinfo] input ex colorimetry : FUTURE COLORIMETRY\n"); + } + + if (pRXStat->input_QuantRange == DEFAULT_RANGE) + printf("[rxinfo] input quant range : DEFAULT\n"); + else if (pRXStat->input_QuantRange == LIMIT_RANGE) + printf("[rxinfo] input quant range : LIMIT\n"); + else if (pRXStat->input_QuantRange == FULL_RANGE) + printf("[rxinfo] input quant range : FULL\n"); + else + printf("[rxinfo] input quant range : RESERVED\n"); + + printf("[rxinfo] input PRfactor %d\n", + pRXStat->input_PRfactor); + } else + printf("[rxinfo] input : DVI signal\n"); + + printf("[rxinfo] input timing info\n"); + + lt8619c_reg_write(0xFF, 0x80); + clk = lt8619c_reg_read(0x44)&0x07; + clk <<= 8; + clk += lt8619c_reg_read(0x45); + clk <<= 8; + clk+=lt8619c_reg_read(0x46); + + printf("[rxinfo] TMDS clock freq : %d kHz\n", clk); + printf("[rxinfo] h_active %d\n", h_active); + printf("[rxinfo] v_active %d\n", v_active); + printf("[rxinfo] h_syncwidth %d\n", h_syncwidth); + printf("[rxinfo] v_syncwidth %d\n", v_syncwidth); + printf("[rxinfo] h_bkporch %d\n", h_bkporch); + printf("[rxinfo] v_bkporch %d\n", v_bkporch); + printf("[rxinfo] h_total %d\n", h_total); + printf("[rxinfo] v_total %d\n", v_total); + printf("[rxinfo] frame_counter %d\n", frame_counter); + + if (h_syncpol) + printf("[rxinfo] h_syncpol is positive\n"); + else + printf("[rxinfo] h_syncpol is negative\n"); + + if (v_syncpol) + printf("[rxinfo] v_syncpol is positive\n"); + else + printf("[rxinfo] v_syncpol is negative\n"); + + lt8619c_reg_write(0xFF, 0x80); + audio_rate = ((lt8619c_reg_read(0x11)&0x03) * 0x100) + + lt8619c_reg_read(0x12); + printf("[rxinfo] audio rate %d kHz\n", audio_rate); + } +} + +int lt8619c_init(void) +{ + int ret; + + /* init variable */ + pRXStat = &RXStat; + memset(pRXStat, 0, sizeof(RXStat)); + + /* reset */ + ret = gpio_request(LT8619_RST_N, "lt8619c_reset"); + if (ret && ret != -EBUSY) { + debug("gpio: requesting pin %u failed\n", LT8619_RST_N); + return ret; + } else { + gpio_direction_output(LT8619_RST_N, 0); + udelay(5000); + gpio_direction_output(LT8619_RST_N, 1); + udelay(10000); + gpio_free(LT8619_RST_N); + } + + /* check chip ID */ + if (!lt8619c_check_chipid()) { + printf("failed to read LT8619C chip id\n"); + return -1; + } + + lt8619c_poweron(); + + if (lt8619c_detect_clk()) { + lt8619c_input_info(); + lt8619c_csc_conversion(); + lt8619c_check_video(); + lt8619c_bt_setting(); + lt8619c_print_rxinfo(); + } + + /* Backlight On */ + ret = gpio_request(BACKLIGHT_GPIO, "backlight"); + if (ret && ret != -EBUSY) { + debug("gpio: requesting pin %u failed\n", BACKLIGHT_GPIO); + return ret; + } else { + gpio_direction_output(BACKLIGHT_GPIO, 1); + udelay(5000); + gpio_free(BACKLIGHT_GPIO); + } + + printf("LT8619C init done\n"); + + return 0; +} diff --git a/board/hardkernel/odroid-common/lt8619c.h b/board/hardkernel/odroid-common/lt8619c.h new file mode 100644 index 0000000000..8c1e79669a --- /dev/null +++ b/board/hardkernel/odroid-common/lt8619c.h @@ -0,0 +1,114 @@ +/* + * LT8619C HDMI to LVDS converter + TFT-LCD 080-QC4011 + * + * Copyright (C) 2020 Hardkernel Co., Ltd. + * + * Author : Joy Cho + * + * The base initial code of this driver is from Lotium semiconductor + * and output signal of LT8619C is fixed as following. + * - resolution 1024x768 + * - 1 port LVDS + * - 8bit, DE timing mode + */ + +#ifndef __LT8619C_H +#define __LT8619C_H + +#define LT8619C_ADDR 0x32 /* 0x64 >> 1 */ + +#if defined(CONFIG_ODROID_N2) +#define LT8619_RST_N GPIOEE(GPIOX_19) +#define BACKLIGHT_GPIO GPIOEE(GPIOX_16) +#else +#define LT8619_RST_N GPIOEE(GPIOH_5) +#define BACKLIGHT_GPIO GPIOEE(GPIOX_16) +#endif + +struct video_timing { + uint32_t pixel_clk; + uint16_t hfp; + uint16_t hs; + uint16_t hbp; + uint16_t hact; + uint16_t htotal; + uint16_t vfp; + uint16_t vs; + uint16_t vbp; + uint16_t vact; + uint16_t vtotal; +}; + +enum LT8619C_OUTPUTMODE_ENUM { + OUTPUT_RGB888 = 0, + OUTPUT_RGB666, + OUTPUT_RGB565, + OUTPUT_YCBCR444, + OUTPUT_YCBCR422_16BIT, + OUTPUT_YCBCR422_20BIT, + OUTPUT_YCBCR422_24BIT, + OUTPUT_BT656_8BIT, + OUTPUT_BT656_10BIT, + OUTPUT_BT656_12BIT, + OUTPUT_BT1120_16BIT, + OUTPUT_BT1120_20BIT, + OUTPUT_BT1120_24BIT, + OUTPUT_LVDS_2_PORT, + OUTPUT_LVDS_1_PORT +}; + +#define LT8619C_OUTPUTMODE OUTPUT_RGB888 + +enum LT8619C_AUDIOINPUT_MODE { + I2S_2CH = 0, + SPDIF +}; + +#define Audio_Input_Mode I2S_2CH + +enum { + COLOR_RGB = 0x00, + COLOR_YCBCR444 = 0x40, + COLOR_YCBCR422 = 0x20 +}; + +#define LT8619C_OUTPUTCOLOR COLOR_RGB + +enum { + NO_DATA = 0x00, + ITU_601 = 0x40, + ITU_709 = 0x80, + EXTENDED_COLORIETRY = 0xc0 +}; + +enum { + xvYCC601 = 0x00, + xvYCC709 = 0x10 + //FUTURE_COLORIETRY +}; + +enum { + DEFAULT_RANGE = 0x00, + LIMIT_RANGE = 0x04, + FULL_RANGE = 0x08, + RESERVED_VAL=0xc0 +}; + +struct _LT8619C_RXStatus { + bool flag_RXClkStable; + bool flag_RXClkDetected; + bool flag_RXPLLLocked; + bool Flag_HsyncStable; + bool input_hdmimode; + uint8_t input_vic; + uint8_t input_colorspace; + uint8_t input_colordepth; + uint8_t input_colorimetry; + uint8_t input_ex_colorimetry; + uint8_t input_QuantRange; + uint8_t input_PRfactor; + uint8_t input_videoindex; + uint32_t ClkFreqValCurrent; + uint32_t ClkFreqValPrevious; +}; +#endif /* __LT8619C_H */ diff --git a/board/hardkernel/odroid-common/odroid-common.h b/board/hardkernel/odroid-common/odroid-common.h index c91af944b9..79c3d62f32 100644 --- a/board/hardkernel/odroid-common/odroid-common.h +++ b/board/hardkernel/odroid-common/odroid-common.h @@ -52,5 +52,6 @@ int board_usbhost_early_power(void); int usbhost_early_poweron(void); int usbhost_gpio_alloc(void); int usbhost_set_power(int on); +int lt8619c_init(void); #endif diff --git a/board/hardkernel/odroid-common/usb.c b/board/hardkernel/odroid-common/usb.c index 1949d446d1..a033b2affa 100644 --- a/board/hardkernel/odroid-common/usb.c +++ b/board/hardkernel/odroid-common/usb.c @@ -53,7 +53,7 @@ static int usbhost_gpio_request(void) debug("gpio: requesting pin %u failed\n", CONFIG_USB_HUB_RST_N); return ret; } - +#if defined(CONFIG_ODROID_N2) /* usb host hub chip enable */ ret = gpio_request(CONFIG_USB_HUB_CHIP_EN, CONFIG_USB_HUB_CHIP_EN_NAME); if (ret && ret != -EBUSY) { @@ -61,7 +61,7 @@ static int usbhost_gpio_request(void) gpio_free(CONFIG_USB_HUB_RST_N); return ret; } - +#endif usbhost_init = true; return 0; @@ -69,7 +69,10 @@ static int usbhost_gpio_request(void) static int usbhost_gpio_free(void) { + +#if defined(CONFIG_ODROID_N2) gpio_free(CONFIG_USB_HUB_CHIP_EN); +#endif gpio_free(CONFIG_USB_HUB_RST_N); return 0; @@ -79,14 +82,19 @@ int usbhost_set_power(int on) { if (on) { if (usbhost_gpio_request() == 0) { + +#if defined(CONFIG_ODROID_N2) gpio_direction_output(CONFIG_USB_HUB_CHIP_EN, 1); udelay(100); +#endif gpio_direction_output(CONFIG_USB_HUB_RST_N, 1); } } else { if (usbhost_requested()) { gpio_direction_output(CONFIG_USB_HUB_RST_N, 0); +#if defined(CONFIG_ODROID_N2) gpio_direction_output(CONFIG_USB_HUB_CHIP_EN, 0); +#endif } usbhost_gpio_free(); diff --git a/board/hardkernel/odroidc4/odroidc4.c b/board/hardkernel/odroidc4/odroidc4.c index 53e202e751..d3f56cc16a 100644 --- a/board/hardkernel/odroidc4/odroidc4.c +++ b/board/hardkernel/odroidc4/odroidc4.c @@ -401,6 +401,13 @@ int board_late_init(void) #ifdef CONFIG_SYS_I2C_AML board_i2c_init(); + if (lt8619c_init() == 0) { + setenv("colorattribute", "rgb,8bit"); + setenv("hdmimode", "1024x600p60hz"); + setenv("outputmode", "1024x600p60hz"); + } else { + printf(" lt8619c hdmi_to_lcd device not found.!\n"); + } #endif setenv("variant", board_is_odroidc4() ? "c4" : "hc4"); board_set_dtbfile("meson64_odroid%s.dtb"); -- cgit v1.2.1