1329 lines
38 KiB
C
1329 lines
38 KiB
C
/*
|
|
Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd.
|
|
*/
|
|
|
|
#include "lt8618sx.h"
|
|
#include <linux/module.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#define I2C_INDEX_0 1
|
|
/*
|
|
* Use the synchronization signal inside the bt1120 signal
|
|
*/
|
|
#define _Embedded_sync_
|
|
/*
|
|
* need to provide external h sync, V sync and de signals to lt8618sxb
|
|
* #define _External_sync_
|
|
*/
|
|
/*
|
|
* IIC Address If CI2CA pins are low(0~400mV)
|
|
*/
|
|
#define _LT8618SX_ADR 0x72
|
|
|
|
#define DEV_NAME "lt8618sx"
|
|
#define DEBUG_LEVEL 1
|
|
#define DPRINTK(level, fmt, args...) \
|
|
do { \
|
|
if (level < DEBUG_LEVEL) \
|
|
printk(KERN_INFO "%s [%s ,%d]: " fmt "\n", DEV_NAME, __FUNCTION__, __LINE__, ##args); \
|
|
} while (0)
|
|
|
|
static struct i2c_client *lt8168sx_client = NULL;
|
|
|
|
static struct i2c_board_info lt8168sx_i2c_info = {
|
|
I2C_BOARD_INFO("lt8168sx", _LT8618SX_ADR >> 1),
|
|
};
|
|
|
|
static int i2c_client_init(void)
|
|
{
|
|
struct i2c_adapter *i2c_adap = NULL;
|
|
|
|
i2c_adap = i2c_get_adapter(I2C_INDEX_0);
|
|
if (i2c_adap == NULL) {
|
|
DPRINTK(0, "i2c%d get adapter failed.\n", I2C_INDEX_0);
|
|
return -1;
|
|
}
|
|
|
|
lt8168sx_client = i2c_new_device(i2c_adap, <8168sx_i2c_info);
|
|
if (lt8168sx_client == NULL) {
|
|
DPRINTK(0, "i2c%d get new device failed.\n", I2C_INDEX_0);
|
|
return -1;
|
|
}
|
|
i2c_put_adapter(i2c_adap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void i2c_client_exit(void)
|
|
{
|
|
if (lt8168sx_client != NULL) {
|
|
i2c_unregister_device(lt8168sx_client);
|
|
lt8168sx_client = NULL;
|
|
}
|
|
}
|
|
|
|
static unsigned char _i2c_read_byte(unsigned char devaddress, unsigned char address)
|
|
{
|
|
#ifdef DRV_I2C
|
|
return drv_i2c_read(I2C_INDEX_0, devaddress, address, 1, 1);
|
|
#else
|
|
unsigned char ret_data = 0xFF;
|
|
int ret;
|
|
struct i2c_client *client = lt8168sx_client;
|
|
struct i2c_msg msg[2];
|
|
unsigned char buffer;
|
|
|
|
(void)devaddress;
|
|
buffer = address;
|
|
msg[0].addr = client->addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = 1;
|
|
msg[0].buf = &buffer;
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].flags = client->flags | I2C_M_RD;
|
|
msg[1].len = 1;
|
|
msg[1].buf = &buffer;
|
|
|
|
ret = bsp_i2c_transfer(client->adapter, msg, 2);
|
|
if (ret < 0) {
|
|
printk("i2c read failed.\n");
|
|
} else {
|
|
ret_data = buffer;
|
|
}
|
|
return ret_data;
|
|
#endif
|
|
}
|
|
|
|
static int _i2c_write_byte(unsigned char devaddress, unsigned char address, unsigned char data)
|
|
{
|
|
#ifdef DRV_I2C
|
|
return drv_i2c_write(I2C_INDEX_0, devaddress, address, 1, data, 1);
|
|
#else
|
|
int ret;
|
|
unsigned char buf[2];
|
|
struct i2c_client *client = lt8168sx_client;
|
|
|
|
(void)devaddress;
|
|
lt8168sx_client->addr = (_LT8618SX_ADR >> 1);
|
|
|
|
buf[0] = address;
|
|
buf[1] = data;
|
|
|
|
ret = i2c_master_send(client, buf, 2);
|
|
if (ret < 0) {
|
|
printk("[%s][%d] i2c write failed.\n", __FUNCTION__, __LINE__);
|
|
}
|
|
udelay(300); /* 300us */
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* --------------------------------
|
|
* CI2CA I2C address
|
|
* --------------------------------
|
|
* (0~400mV)----------0x72(default)
|
|
* (400mV~800mV)------0x7a
|
|
* (800mV~1200mV)-----0x90
|
|
* (1200mV~1600mV) ---0x92
|
|
*
|
|
* (2000mV~2400mV)----0x94
|
|
* (2400mV~2800mV)----0x7e
|
|
* (2800mV~3300mV)----0x76
|
|
* -------------------------------
|
|
*/
|
|
/*
|
|
* The lowest bit 0 of the IIC address 0x72 (0x76) of LT8618SXB is the read-write flag bit.
|
|
* In the case of Linux IIC, the highest bit is the read-write flag bit, The IIC address needs to be moved one bit to
|
|
* the right, and the IIC address becomes 0x39 (0x3B). IIC rate should not exceed 100K. If the IIC address of LT8618SXB
|
|
* is not 0x72, you need to reset LT8618SXB with the master GPIO, pull down 100ms, then pull up, delay 100ms, and
|
|
* initialize the LT8618SXB register.
|
|
*/
|
|
static void HDMI_WriteI2C_Byte(unsigned char reg_addr, unsigned char value)
|
|
{
|
|
_i2c_write_byte(_LT8618SX_ADR, reg_addr, value);
|
|
return;
|
|
}
|
|
unsigned int HDMI_ReadI2C_Byte(unsigned char reg_addr)
|
|
{
|
|
unsigned char read_ret = _i2c_read_byte(_LT8618SX_ADR, reg_addr);
|
|
return read_ret;
|
|
}
|
|
|
|
/*
|
|
* 1 Reset LT8618SX
|
|
* 2 LT8618SX Initial setting:
|
|
* u16 hfp, hs_width, hbp, h_act, h_tal, v_act, v_tal, vfp, vs_width, vbp;
|
|
* u8 HDMI_VIC = 0x00;
|
|
* u8 HDMI_Y = 0x00;
|
|
* bool hs_pol, vs_pol;
|
|
*/
|
|
/*
|
|
* LT8618SXB_D0_D7_Bit_Swap, 0x01: Red D0 ~ D7 swap enable; 0x00 : Normal
|
|
* LT8618SXB_D8_D15_Bit_Swap, 0x02: Red D8 ~ D15 swap enable; 0x00 : Normal
|
|
* LT8618SXB_D16_D23_Bit_Swap, 0x04: Red D16 ~ D23 swap enable; 0x00 : Normal
|
|
*/
|
|
#define LT8618SXB_D0_D7_Bit_Swap 0x00
|
|
#define LT8618SXB_D8_D15_Bit_Swap 0x00
|
|
#define LT8618SXB_D16_D23_Bit_Swap 0x00
|
|
|
|
/*
|
|
* _D0_D15_In, BT1120 input from D0 to D15 of LT8618SXB pins. D0 ~ D7 Y ; D8 ~ D15 C
|
|
* _D8_D23_In, BT1120 input from D8 to D23 of LT8618SXB pins. D8 ~ D15 Y ; D16 ~ D23 C
|
|
* _D0_D15_In_2, BT1120 input from D0 to D15 of LT8618SXB pins. D0 ~ D7 C ; D8 ~ D15 Y
|
|
* _D8_D23_In_2, BT1120 input from D8 to D23 of LT8618SXB pins. D8 ~ D15 C ; D16 ~ D23 Y
|
|
*/
|
|
#define _D0_D15_In 0x30
|
|
#define _D8_D23_In 0x70
|
|
#define _D0_D15_In_2 0x00
|
|
#define _D8_D23_In_2 0x60
|
|
|
|
/*
|
|
* 1: DDR mode; 0: SDR (normal) mode
|
|
*/
|
|
bool Use_DDRCLK = 0;
|
|
module_param(Use_DDRCLK, bool, S_IRUGO);
|
|
|
|
u8 I2CADR;
|
|
|
|
/*
|
|
* vic ,0x10: 1080P ; 0x04 : 720P ; Refer to the following list
|
|
*/
|
|
u8 VIC_Num = 16;
|
|
|
|
#ifdef _Read_TV_EDID_
|
|
|
|
u8 Sink_EDID[256];
|
|
|
|
#endif
|
|
|
|
/*
|
|
* --------------------------------------
|
|
* Resolution VIC_Num
|
|
* --------------------------------------
|
|
* 640x480 1
|
|
* 720x480P 60Hz 2
|
|
* 720x480i 60Hz 6
|
|
* 720x576P 50Hz 17
|
|
* 720x576i 50Hz 21
|
|
* 1280x720P 24Hz 60
|
|
* 1280x720P 25Hz 61
|
|
* 1280x720P 30Hz 62
|
|
* 1280x720P 50Hz 19
|
|
* 1280x720P 60Hz 4
|
|
* 1920x1080P 24Hz 32
|
|
* 1920x1080P 25Hz 33
|
|
* 1920x1080P 30Hz 34
|
|
* 1920x1080i 50Hz 20
|
|
* 1920x1080i 60Hz 5
|
|
* 1920x1080P 50Hz 31
|
|
* 1920x1080P 60Hz 16
|
|
* other resolution 0(default)
|
|
* --------------------------------------
|
|
*/
|
|
#define VIC_640X480 1
|
|
#define VIC_720X480P60Hz 2
|
|
#define VIC_720X480I60Hz 6
|
|
#define VIC_720X576P50Hz 17
|
|
#define VIC_720X576I50Hz 21
|
|
#define VIC_1280X720P24Hz 60
|
|
#define VIC_1280X720P25Hz 61
|
|
#define VIC_1280X720P30Hz 62
|
|
#define VIC_1280X720P50Hz 19
|
|
#define VIC_1280X720P60Hz 4
|
|
#define VIC_1920X1080P24Hz 32
|
|
#define VIC_1920X1080P25Hz 33
|
|
#define VIC_1920X1080P30Hz 34
|
|
#define VIC_1920X1080I50Hz 20
|
|
#define VIC_1920X1080I60Hz 5
|
|
#define VIC_1920X1080P50Hz 31
|
|
#define VIC_1920X1080P60Hz 16
|
|
#define VIC_3840x2160_24Hz 93
|
|
#define VIC_3840x2160_25Hz 94
|
|
#define VIC_3840x2160_30Hz 95
|
|
#define VIC_DEFAULT 0
|
|
|
|
enum {
|
|
_32KHz = 0,
|
|
_44d1KHz = 1,
|
|
_48KHz = 2,
|
|
_88d2KHz = 3,
|
|
_96KHz = 4,
|
|
_176Khz = 5,
|
|
_196KHz = 6,
|
|
|
|
FREQ_BUTT = 7,
|
|
};
|
|
|
|
/*
|
|
* 4096 : 32K
|
|
* 6272 : 44.1K
|
|
* 6144 : 48K
|
|
* 12544 : 88.2K
|
|
* 12288 : 96K
|
|
* 25088 : 176K
|
|
* 24576 : 196K
|
|
*/
|
|
u16 IIS_N[FREQ_BUTT] = {
|
|
4096,
|
|
6272,
|
|
6144,
|
|
12544,
|
|
12288,
|
|
25088,
|
|
24576,
|
|
};
|
|
|
|
/*
|
|
* 0x30 : 32K
|
|
* 0x00 : 44.1K
|
|
* 0x20 : 48K
|
|
* 0x80 : 88.2K
|
|
* 0xa0 : 96K
|
|
* 0xc0 : 176K
|
|
* 0xe0 : 196K
|
|
*/
|
|
u16 Sample_Freq[FREQ_BUTT] = {
|
|
0x30,
|
|
0x00,
|
|
0x20,
|
|
0x80,
|
|
0xa0,
|
|
0xc0,
|
|
0xe0,
|
|
};
|
|
|
|
typedef enum {
|
|
LT_OUT_PAL = 0, /* PAL standard */
|
|
LT_OUT_NTSC = 1, /* NTSC standard */
|
|
LT_OUT_960H_PAL = 2, /* ITU-R BT.1302 960 x 576 at 50 Hz (interlaced) */
|
|
LT_OUT_960H_NTSC = 3, /* ITU-R BT.1302 960 x 480 at 60 Hz (interlaced) */
|
|
|
|
LT_OUT_640x480_60 = 4, /* VESA 640 x 480 at 60 Hz (non-interlaced) CVT */
|
|
LT_OUT_480P60 = 5, /* 720 x 480 at 60 Hz. */
|
|
LT_OUT_576P50 = 6, /* 720 x 576 at 50 Hz. */
|
|
LT_OUT_800x600_60 = 7, /* VESA 800 x 600 at 60 Hz (non-interlaced) */
|
|
LT_OUT_1024x768_60 = 8, /* VESA 1024 x 768 at 60 Hz (non-interlaced) */
|
|
LT_OUT_720P50 = 9, /* 1280 x 720 at 50 Hz. */
|
|
LT_OUT_720P60 = 10, /* 1280 x 720 at 60 Hz. */
|
|
LT_OUT_1280x800_60 = 11, /* 1280*800@60Hz VGA@60Hz */
|
|
LT_OUT_1280x1024_60 = 12, /* VESA 1280 x 1024 at 60 Hz (non-interlaced) */
|
|
LT_OUT_1366x768_60 = 13, /* VESA 1366 x 768 at 60 Hz (non-interlaced) */
|
|
LT_OUT_1400x1050_60 = 14, /* VESA 1400 x 1050 at 60 Hz (non-interlaced) CVT */
|
|
LT_OUT_1440x900_60 = 15, /* VESA 1440 x 900 at 60 Hz (non-interlaced) CVT Compliant */
|
|
LT_OUT_1680x1050_60 = 16, /* VESA 1680 x 1050 at 60 Hz (non-interlaced) */
|
|
|
|
LT_OUT_1080P24 = 17, /* 1920 x 1080 at 24 Hz. */
|
|
LT_OUT_1080P25 = 18, /* 1920 x 1080 at 25 Hz. */
|
|
LT_OUT_1080P30 = 19, /* 1920 x 1080 at 30 Hz. */
|
|
LT_OUT_1080I50 = 20, /* 1920 x 1080 at 50 Hz, interlaced. */
|
|
LT_OUT_1080I60 = 21, /* 1920 x 1080 at 60 Hz, interlaced. */
|
|
LT_OUT_1080P50 = 22, /* 1920 x 1080 at 50 Hz. */
|
|
LT_OUT_1080P60 = 23, /* 1920 x 1080 at 60 Hz. */
|
|
|
|
LT_OUT_1600x1200_60 = 24, /* VESA 1600 x 1200 at 60 Hz (non-interlaced) */
|
|
LT_OUT_1920x1200_60 = 25, /* VESA 1920 x 1600 at 60 Hz (non-interlaced) CVT (Reduced Blanking) */
|
|
LT_OUT_1920x2160_30 = 26, /* 1920x2160_30 */
|
|
LT_OUT_2560x1440_30 = 27, /* 2560x1440_30 */
|
|
LT_OUT_2560x1440_60 = 28, /* 2560x1440_60 */
|
|
LT_OUT_2560x1600_60 = 29, /* 2560x1600_60 */
|
|
|
|
LT_OUT_3840x2160_24 = 30, /* 3840x2160_24 */
|
|
LT_OUT_3840x2160_25 = 31, /* 3840x2160_25 */
|
|
LT_OUT_3840x2160_30 = 32, /* 3840x2160_30 */
|
|
|
|
LT_OUT_USER = 33, /* User timing. */
|
|
|
|
LT_OUT_BUTT = 34,
|
|
} lt_intf_sync;
|
|
|
|
enum {
|
|
Less_than_50M = 0,
|
|
Bound_50_60M = 1,
|
|
Bound_60_90M = 2,
|
|
Bound_90_100M = 3,
|
|
Bound_100_120M = 4,
|
|
Bound_120_150M = 5,
|
|
Bound_150_180M = 6,
|
|
Bound_180_200M = 7,
|
|
Bound_200_210M = 8,
|
|
Bound_210_240M = 9,
|
|
Bound_240_270M = 10,
|
|
More_than_270M = 11,
|
|
|
|
BOUND_BUTT = 12,
|
|
};
|
|
|
|
static u8 Resolution_Num = 24;
|
|
#define _16_9_ 0x2A
|
|
#define _4_3_ 0x19
|
|
|
|
enum {
|
|
H_act = 0,
|
|
V_act = 1,
|
|
H_tol = 2,
|
|
V_tol = 3,
|
|
|
|
H_bp = 4,
|
|
H_sync = 5,
|
|
H_fp = 6,
|
|
|
|
V_fp = 7,
|
|
V_sync = 8,
|
|
V_bp = 9,
|
|
Vic = 10,
|
|
Pic_Ratio = 11, /* Image proportion */
|
|
Clk_bound_SDR = 12, /* SDR */
|
|
Clk_bound_DDR = 13, /* DDR */
|
|
|
|
TIMING_BUTT = 14,
|
|
};
|
|
|
|
static int Format_Timing[LT_OUT_BUTT][TIMING_BUTT] = {
|
|
/* H_act V_act H_total V_total H_BP H_sync H_FP V_FP V_sync V_BP Vic Pic_Ratio Clk_bound_SDR Clk_bound_DDR */
|
|
{720, 288, 864, 312, 6, 126, 12, 2, 3, 19, VIC_720X576I50Hz, _4_3_, Less_than_50M, Less_than_50M},
|
|
{720, 240, 858, 262, 0, 124, 19, 4, 3, 15, VIC_720X480I60Hz, _4_3_, Less_than_50M, Less_than_50M},
|
|
{960, 288, 1152, 312, 8, 168, 16, 2, 3, 19, VIC_DEFAULT, _4_3_, Less_than_50M, Less_than_50M},
|
|
{960, 240, 1144, 262, 0, 168, 21, 4, 3, 15, VIC_DEFAULT, _4_3_, Less_than_50M, Less_than_50M},
|
|
{640, 480, 800, 525, 48, 96, 16, 10, 2, 33, VIC_640X480, _4_3_, Less_than_50M, Less_than_50M},
|
|
{720, 480, 858, 525, 60, 62, 16, 9, 6, 30, VIC_720X480P60Hz, _4_3_, Less_than_50M, Less_than_50M},
|
|
{720, 576, 864, 625, 68, 64, 12, 5, 5, 39, VIC_720X576P50Hz, _4_3_, Less_than_50M, Less_than_50M},
|
|
{800, 600, 1056, 628, 88, 128, 40, 1, 4, 23, VIC_DEFAULT, _4_3_, Less_than_50M, Less_than_50M},
|
|
{1024, 768, 1344, 806, 160, 136, 24, 3, 6, 29, VIC_DEFAULT, _4_3_, Bound_60_90M, Less_than_50M},
|
|
{1280, 720, 1980, 750, 220, 40, 440, 5, 5, 20, VIC_1280X720P50Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1280, 720, 1650, 750, 220, 40, 110, 5, 5, 20, VIC_1280X720P60Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1280, 800, 1680, 831, 200, 128, 72, 3, 6, 22, VIC_DEFAULT, _4_3_, Bound_60_90M, Less_than_50M},
|
|
{1280, 1024, 1688, 1066, 248, 112, 48, 1, 3, 38, VIC_DEFAULT, _4_3_, Bound_100_120M, Bound_50_60M},
|
|
{1366, 768, 1792, 798, 213, 143, 70, 3, 3, 24, VIC_DEFAULT, _4_3_, Bound_60_90M, Less_than_50M},
|
|
{1400, 1050, 1864, 1089, 232, 144, 88, 3, 4, 32, VIC_DEFAULT, _4_3_, Bound_120_150M, Bound_60_90M},
|
|
{1440, 900, 1904, 934, 232, 152, 80, 3, 6, 25, VIC_DEFAULT, _4_3_, Bound_100_120M, Bound_50_60M},
|
|
{1680, 1050, 2240, 1089, 280, 176, 104, 3, 6, 30, VIC_DEFAULT, _4_3_, Bound_120_150M, Bound_60_90M},
|
|
{1920, 1080, 2750, 1125, 148, 44, 638, 4, 5, 36, VIC_1920X1080P24Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1920, 1080, 2640, 1125, 148, 44, 528, 4, 5, 36, VIC_1920X1080P25Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1920, 1080, 2200, 1125, 148, 44, 88, 4, 5, 36, VIC_1920X1080P30Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1920, 540, 2640, 562, 148, 44, 528, 2, 5, 15, VIC_1920X1080I50Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1920, 540, 2200, 562, 148, 44, 88, 2, 5, 15, VIC_1920X1080I60Hz, _16_9_, Bound_60_90M, Less_than_50M},
|
|
{1920, 1080, 2640, 1125, 148, 44, 528, 4, 5, 36, VIC_1920X1080P50Hz, _16_9_, Bound_120_150M, Bound_60_90M},
|
|
{1920, 1080, 2200, 1125, 148, 44, 88, 4, 5, 36, VIC_1920X1080P60Hz, _16_9_, Bound_120_150M, Bound_60_90M},
|
|
{1600, 1200, 2160, 1250, 304, 192, 64, 1, 3, 46, VIC_DEFAULT, _4_3_, Bound_150_180M, Bound_60_90M},
|
|
{1920, 1200, 2080, 1235, 80, 32, 48, 3, 6, 26, VIC_1920X1080P60Hz, _16_9_, Bound_150_180M, Bound_60_90M},
|
|
{1920, 2160, 2200, 2240, 148, 44, 88, 8, 5, 67, VIC_1920X1080P60Hz, _4_3_, Bound_120_150M, Bound_60_90M},
|
|
{2560, 1440, 2720, 1481, 80, 32, 48, 2, 5, 34, VIC_DEFAULT, _4_3_, Bound_120_150M, Bound_60_90M},
|
|
{2560, 1440, 2720, 1481, 80, 32, 48, 2, 5, 34, VIC_DEFAULT, _4_3_, Bound_240_270M, Bound_120_150M},
|
|
{2560, 1600, 2720, 1646, 80, 32, 48, 3, 6, 37, VIC_DEFAULT, _4_3_, Bound_240_270M, Bound_120_150M},
|
|
{3840, 2160, 5500, 2250, 296, 88, 1276, 8, 10, 72, VIC_3840x2160_24Hz, _16_9_, More_than_270M, Bound_120_150M},
|
|
{3840, 2160, 5280, 2250, 296, 88, 1056, 8, 10, 72, VIC_3840x2160_25Hz, _16_9_, More_than_270M, Bound_120_150M},
|
|
{3840, 2160, 4400, 2250, 296, 88, 176, 8, 10, 72, VIC_3840x2160_30Hz, _16_9_, More_than_270M, Bound_120_150M},
|
|
{0}, /* user */
|
|
};
|
|
|
|
#ifdef _External_sync_
|
|
u16 hfp, hs_width, hbp, h_act, h_tal, v_act, v_tal, vfp, vs_width, vbp;
|
|
bool hs_pol, vs_pol;
|
|
#else
|
|
u16 h_act, h_tal, v_act;
|
|
#endif
|
|
u32 CLK_Cnt;
|
|
|
|
static u8 LT8618SXB_BT1120_SDR_PLL_setting[BOUND_BUTT][3] = {
|
|
{0x00, 0xa8, 0xbb}, /* < 50MHz */
|
|
{0x00, 0x94, 0xaa}, /* 50 ~ 59M */
|
|
{0x01, 0xa8, 0xaa}, /* 60 ~ 89M */
|
|
{0x02, 0xbc, 0xaa}, /* 90 ~ 99M */
|
|
|
|
{0x02, 0x9e, 0x99}, /* 100 ~ 119M */
|
|
{0x03, 0xa8, 0x99}, /* 120 - 149M */
|
|
{0x04, 0xb2, 0x99}, /* 150 - 179M */
|
|
{0x05, 0xbc, 0x99}, /* 180 - 199M */
|
|
|
|
{0x05, 0x9e, 0x88}, /* 200 - 209M */
|
|
{0x06, 0xa3, 0x88}, /* 210 - 239M */
|
|
{0x07, 0xa8, 0x88}, /* 240 - 269M */
|
|
{0x08, 0xad, 0x88}, /* >= 270 M */
|
|
};
|
|
|
|
/*
|
|
* BT1120 720P 60Hz DDR, D_CLK is 37MHz, select Less_than_50M
|
|
* BT1120 1080P 60Hz DDR, D_CLK is 74.25MHz, select Bound_60_90M.
|
|
* BT1120 4K30Hz DDR, D_CLK is 148.5MHz, select Bound_120_150M
|
|
* {0x00, 0xd0, 0xbb}, < 25MHz
|
|
*/
|
|
|
|
static u8 LT8618SXB_BT1120_DDR_PLL_setting[12][3] = {
|
|
{0x00, 0xa8, 0xaa}, /* 25 ~ 50M */
|
|
{0x00, 0x94, 0x99}, /* 50 ~ 59M */
|
|
{0x01, 0xa8, 0x99}, /* 60 ~ 89M */
|
|
|
|
{0x02, 0xbc, 0x99}, /* 90 ~ 99M */
|
|
{0x02, 0x9e, 0x88}, /* 100 ~ 119M */
|
|
{0x03, 0xa8, 0x88}, /* 120 - 149M */
|
|
{0x04, 0xb2, 0x88}, /* 150 - 179M */
|
|
|
|
{0x05, 0xbc, 0x88}, /* 180 - 209M */
|
|
{0x06, 0xc6, 0x88}, /* 210 - 239M */
|
|
{0x07, 0xd0, 0x88}, /* 240 - 269M */
|
|
{0x08, 0xda, 0x88}, /* >= 270M */
|
|
};
|
|
u8 CLK_bound;
|
|
|
|
/*
|
|
* SDR:1080P 60/50Hz; DDR:4K30Hz. -- 148.5M
|
|
*/
|
|
u8 CLK_Num = Bound_120_150M;
|
|
|
|
static unsigned int norm = 24; /* 24: 1080P60 */
|
|
|
|
module_param(norm, int, S_IRUGO);
|
|
|
|
static void LT8618SX_VideoSel(unsigned int vmode)
|
|
{
|
|
if (vmode > LT_OUT_USER) {
|
|
/* defalut: 1080P60 */
|
|
VIC_Num = VIC_1920X1080P60Hz;
|
|
/* 6: HDMI_1080P60 */
|
|
Resolution_Num = HDMI_1080P60;
|
|
/* 148.5M */
|
|
CLK_Num = Bound_120_150M;
|
|
printk("Havn't the mode:%d\n", vmode);
|
|
return;
|
|
}
|
|
if (Format_Timing[vmode][H_act] == 0) {
|
|
printk("\nwarning: video format(%u)'s timing must be set.\n\n", vmode);
|
|
}
|
|
Resolution_Num = vmode;
|
|
VIC_Num = Format_Timing[vmode][Vic];
|
|
CLK_Num = Format_Timing[vmode][Clk_bound_SDR];
|
|
}
|
|
|
|
#if LT_DESC("open/close/ioctl")
|
|
|
|
static unsigned int open_cnt = 0;
|
|
|
|
static int lt_open(struct inode *inode, struct file *file)
|
|
{
|
|
(void)inode;
|
|
(void)file;
|
|
|
|
if ((open_cnt++) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
DPRINTK(0, "you should close the device first!");
|
|
return -1;
|
|
}
|
|
|
|
static void lt_device_init(u32 norm_tmp)
|
|
{
|
|
Resolution_change(norm_tmp);
|
|
}
|
|
|
|
static int lt_close(struct inode *inode, struct file *file)
|
|
{
|
|
(void)inode;
|
|
(void)file;
|
|
|
|
open_cnt--;
|
|
return 0;
|
|
}
|
|
|
|
static long lt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
u32 norm_tmp = 0;
|
|
u32 use_ddr_clk = 0;
|
|
|
|
(void)file;
|
|
|
|
if ((char *)(uintptr_t)arg == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (((_IOC_SIZE(cmd) == 0) && (_IOC_DIR(cmd) != _IOC_NONE))) {
|
|
printk("%s %d - input param err, size of cmd is 0!\n", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if ((_IOC_DIR(cmd) != _IOC_NONE) && (((char *)(uintptr_t)arg) == NULL)) {
|
|
printk("%s %d - input param err, arg is null!\n", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (_IOC_DIR(cmd) != _IOC_WRITE) {
|
|
printk("%s %d - input param err, cmd is not write!\n", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case (LT_CMD_SETCLK): {
|
|
if (copy_from_user(&use_ddr_clk, (void *)(uintptr_t)arg, sizeof(unsigned int))) {
|
|
return -1;
|
|
}
|
|
|
|
if ((use_ddr_clk >= LT_CLK_EDGE_SINGLE) && (norm_tmp <= LT_CLK_EDGE_DUAL)) {
|
|
Use_DDRCLK = use_ddr_clk;
|
|
return 0;
|
|
} else {
|
|
printk("%s havn't the Use_DDRCLK:%u\n", DEV_NAME, use_ddr_clk);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case (LT_CMD_SETMODE): {
|
|
if (copy_from_user(&norm_tmp, (void *)(uintptr_t)arg, sizeof(unsigned int))) {
|
|
return -1;
|
|
}
|
|
|
|
if ((norm_tmp >= LT_OUT_640x480_60) && (norm_tmp < LT_OUT_USER)) {
|
|
lt_device_init(norm_tmp);
|
|
return 0;
|
|
} else {
|
|
printk("%s havn't the mode:%u\n", DEV_NAME, norm_tmp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
printk("%s havn't the cmd:0x%x\n", DEV_NAME, cmd);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct file_operations lt_fops = {
|
|
.owner = THIS_MODULE,
|
|
.unlocked_ioctl = lt_ioctl,
|
|
.open = lt_open,
|
|
.release = lt_close
|
|
};
|
|
|
|
static struct miscdevice lt_dev = {
|
|
MISC_DYNAMIC_MINOR,
|
|
DEV_NAME,
|
|
<_fops,
|
|
};
|
|
#endif
|
|
|
|
static void lt_reset_ttl(void)
|
|
{
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
HDMI_WriteI2C_Byte(0xee, 0x01);
|
|
|
|
/* reset MIPI Rx logic. */
|
|
HDMI_WriteI2C_Byte(0x11, 0x00);
|
|
|
|
HDMI_WriteI2C_Byte(0x13, 0xf1);
|
|
}
|
|
|
|
static int lt_reboot(struct notifier_block *self, unsigned long event, void *data)
|
|
{
|
|
(void)self;
|
|
(void)event;
|
|
(void)data;
|
|
lt_reset_ttl();
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block lt_reboot_notifier = {
|
|
.notifier_call = lt_reboot,
|
|
};
|
|
|
|
static int lt_reboot_init(void)
|
|
{
|
|
register_reboot_notifier(<_reboot_notifier);
|
|
return 0;
|
|
}
|
|
|
|
static void lt_reboot_exit(void)
|
|
{
|
|
unregister_reboot_notifier(<_reboot_notifier);
|
|
}
|
|
|
|
#ifdef _LT8618_HDCP_
|
|
|
|
static void LT8618SX_HDCP_Init(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x85);
|
|
HDMI_WriteI2C_Byte(0x07, 0x1f);
|
|
/*
|
|
* [7]=force_hpd, [6]=force_rsen, [5]=vsync_pol, [4]=hsync_pol,
|
|
* [3]=hdmi_mode, [2]=no_accs_when_rdy, [1]=skip_wt_hdmi
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x13, 0xfe);
|
|
|
|
/*
|
|
* [7]=ri_short_read, [3]=sync_pol_mode, [2]=srm_chk_done,
|
|
* [1]=bksv_srm_pass, [0]=ksv_list_vld
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x17, 0x0f);
|
|
|
|
HDMI_WriteI2C_Byte(0x15, 0x05);
|
|
}
|
|
|
|
static void LT8618SX_HDCP_Enable(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
HDMI_WriteI2C_Byte(0x14, 0x7f);
|
|
HDMI_WriteI2C_Byte(0x14, 0xff);
|
|
HDMI_WriteI2C_Byte(0xff, 0x85);
|
|
/* disable HDCP */
|
|
HDMI_WriteI2C_Byte(0x15, 0x01);
|
|
/* enable HDCP */
|
|
HDMI_WriteI2C_Byte(0x15, 0x71);
|
|
/* enable HDCP */
|
|
HDMI_WriteI2C_Byte(0x15, 0x65);
|
|
}
|
|
|
|
static void LT8618SX_HDCP_Disable(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x85);
|
|
/* enable HDCP */
|
|
HDMI_WriteI2C_Byte(0x15, 0x45);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void LT8918SXB_Read_EDID(void)
|
|
{
|
|
#ifdef _Read_TV_EDID_
|
|
|
|
u8 i, j;
|
|
u8 extended_flag = 0x00;
|
|
|
|
HDMI_WriteI2C_Byte(0xff, 0x85);
|
|
/*
|
|
* I2C 100K
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x03, 0xc9);
|
|
/* 0xA0 is EDID device address */
|
|
HDMI_WriteI2C_Byte(0x04, 0xa0);
|
|
/* 0x00 is EDID offset address */
|
|
HDMI_WriteI2C_Byte(0x05, 0x00);
|
|
/* length for read */
|
|
HDMI_WriteI2C_Byte(0x06, 0x20);
|
|
HDMI_WriteI2C_Byte(0x14, 0x7f);
|
|
|
|
/* block 0 & 1 */
|
|
for (i = 0; i < 8; i++) {
|
|
/* 0x00 is EDID offset address */
|
|
HDMI_WriteI2C_Byte(0x05, i * 32);
|
|
HDMI_WriteI2C_Byte(0x07, 0x36);
|
|
/* 0x31 */
|
|
HDMI_WriteI2C_Byte(0x07, 0x34);
|
|
/* 0x37 */
|
|
HDMI_WriteI2C_Byte(0x07, 0x37);
|
|
/* wait 5ms for reading edid data. */
|
|
Timer0_Delay1ms(5);
|
|
if (HDMI_ReadI2C_Byte(0x40) & 0x02) {
|
|
/* DDC No Ack or Abitration lost */
|
|
if (HDMI_ReadI2C_Byte(0x40) & 0x50) {
|
|
printk("\r\nread edid failed: no ack");
|
|
goto end;
|
|
} else {
|
|
printk("\r\n");
|
|
for (j = 0; j < 32; j++) {
|
|
Sink_EDID[i * 32 + j] = HDMI_ReadI2C_Byte(0x83);
|
|
printk("%u ", Sink_EDID[i * 32 + j]);
|
|
if ((i == 3) && (j == 30)) {
|
|
extended_flag = Sink_EDID[i * 32 + j] & 0x03;
|
|
}
|
|
}
|
|
if (i == 3) {
|
|
/* no block 1, stop reading edid. */
|
|
if (extended_flag < 1) {
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
printk("\r\nread edid failed: accs not done");
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* no block 2, stop reading edid. */
|
|
if (extended_flag < 2) {
|
|
goto end;
|
|
}
|
|
|
|
/* block 2 & 3 */
|
|
for (i = 0; i < 8; i++) {
|
|
/* 0x00 is EDID offset address */
|
|
HDMI_WriteI2C_Byte(0x05, i * 32);
|
|
HDMI_WriteI2C_Byte(0x07, 0x76);
|
|
HDMI_WriteI2C_Byte(0x07, 0x74);
|
|
HDMI_WriteI2C_Byte(0x07, 0x77);
|
|
/* wait 5ms for reading edid data. */
|
|
Timer0_Delay1ms(5);
|
|
if (HDMI_ReadI2C_Byte(0x40) & 0x02) {
|
|
/* DDC No Ack or Abitration lost */
|
|
if (HDMI_ReadI2C_Byte(0x40) & 0x50) {
|
|
printk("\r\nread edid failed: no ack");
|
|
goto end;
|
|
} else {
|
|
printk("\r\n");
|
|
for (j = 0; j < 32; j++) {
|
|
Sink_EDID2[i * 32 + j] = HDMI_ReadI2C_Byte(0x83);
|
|
printk("%u ", Sink_EDID2[i * 32 + j]);
|
|
}
|
|
if (i == 3) {
|
|
/* no block 1, stop reading edid. */
|
|
if (extended_flag < 3) {
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
printk("\r\nread edid failed: accs not done");
|
|
goto end;
|
|
}
|
|
}
|
|
end:
|
|
HDMI_WriteI2C_Byte(0x03, 0xc2);
|
|
HDMI_WriteI2C_Byte(0x07, 0x1f);
|
|
#endif
|
|
}
|
|
|
|
static void LT8618SXB_Video_check(void)
|
|
{
|
|
/* video */
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
|
|
#ifdef _Embedded_sync_
|
|
|
|
h_tal = HDMI_ReadI2C_Byte(0x8f);
|
|
h_tal = (h_tal << 8) + HDMI_ReadI2C_Byte(0x90);
|
|
|
|
v_act = HDMI_ReadI2C_Byte(0x8b);
|
|
v_act = (v_act << 8) + HDMI_ReadI2C_Byte(0x8c);
|
|
|
|
h_act = HDMI_ReadI2C_Byte(0x8d);
|
|
h_act = (h_act << 8) + HDMI_ReadI2C_Byte(0x8e);
|
|
|
|
CLK_Cnt = (HDMI_ReadI2C_Byte(0x1d) & 0x0f) * 0x10000 + HDMI_ReadI2C_Byte(0x1e) * 0x100 + HDMI_ReadI2C_Byte(0x1f);
|
|
#else
|
|
u8 temp;
|
|
|
|
vs_pol = 0;
|
|
hs_pol = 0;
|
|
|
|
/* hs vs polarity */
|
|
temp = HDMI_ReadI2C_Byte(0x70);
|
|
if (temp & 0x02) {
|
|
vs_pol = 1;
|
|
}
|
|
if (temp & 0x01) {
|
|
hs_pol = 1;
|
|
}
|
|
|
|
vs_width = HDMI_ReadI2C_Byte(0x71);
|
|
|
|
hs_width = HDMI_ReadI2C_Byte(0x72);
|
|
hs_width = (hs_width << 8) + HDMI_ReadI2C_Byte(0x73);
|
|
|
|
vbp = HDMI_ReadI2C_Byte(0x74);
|
|
vfp = HDMI_ReadI2C_Byte(0x75);
|
|
|
|
hbp = HDMI_ReadI2C_Byte(0x76);
|
|
hbp = (hbp << 8) + HDMI_ReadI2C_Byte(0x77);
|
|
|
|
hfp = HDMI_ReadI2C_Byte(0x78);
|
|
hfp = (hfp << 8) + HDMI_ReadI2C_Byte(0x79);
|
|
|
|
v_tal = HDMI_ReadI2C_Byte(0x7a);
|
|
v_tal = (v_tal << 8) + HDMI_ReadI2C_Byte(0x7b);
|
|
|
|
h_tal = HDMI_ReadI2C_Byte(0x7c);
|
|
h_tal = (h_tal << 8) + HDMI_ReadI2C_Byte(0x7d);
|
|
|
|
v_act = HDMI_ReadI2C_Byte(0x7e);
|
|
v_act = (v_act << 8) + HDMI_ReadI2C_Byte(0x7f);
|
|
|
|
h_act = HDMI_ReadI2C_Byte(0x80);
|
|
h_act = (h_act << 8) + HDMI_ReadI2C_Byte(0x81);
|
|
|
|
CLK_Cnt = (HDMI_ReadI2C_Byte(0x1d) & 0x0f) * 0x10000 + HDMI_ReadI2C_Byte(0x1e) * 0x100 + HDMI_ReadI2C_Byte(0x1f);
|
|
#endif
|
|
}
|
|
|
|
static void LT8618SXB_TTL_Input_Analog(void)
|
|
{
|
|
/* TTL mode */
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x81);
|
|
HDMI_WriteI2C_Byte(0x02, 0x66);
|
|
HDMI_WriteI2C_Byte(0x0a, 0x06);
|
|
HDMI_WriteI2C_Byte(0x15, 0x06);
|
|
|
|
/* for U2 */
|
|
HDMI_WriteI2C_Byte(0x4e, 0xa8);
|
|
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
HDMI_WriteI2C_Byte(0x1b, 0x77);
|
|
/* 25000 */
|
|
HDMI_WriteI2C_Byte(0x1c, 0xEC);
|
|
}
|
|
|
|
static void LT8618SXB_TTL_Input_Digtal(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
#ifdef _Embedded_sync_
|
|
/* Internal generate sync/de control logic clock enable */
|
|
HDMI_WriteI2C_Byte(0x0A, 0xF0);
|
|
#else
|
|
/* _External_sync_ */
|
|
HDMI_WriteI2C_Byte(0x0A, 0xC0);
|
|
#endif
|
|
|
|
/* TTL_Input_Digtal */
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
/* YC channel swap */
|
|
HDMI_WriteI2C_Byte(0x45, _D8_D23_In + LT8618SXB_D0_D7_Bit_Swap + LT8618SXB_D16_D23_Bit_Swap +
|
|
LT8618SXB_D8_D15_Bit_Swap);
|
|
|
|
if (Use_DDRCLK) {
|
|
/* 0x80: dclk */
|
|
HDMI_WriteI2C_Byte(0x4f, 0x80);
|
|
} else {
|
|
/* 0x40: txpll_clk */
|
|
HDMI_WriteI2C_Byte(0x4f, 0x40);
|
|
}
|
|
HDMI_WriteI2C_Byte(0x50, 0x00);
|
|
#ifdef _Embedded_sync_
|
|
|
|
/* Select BT rx decode det_vs/hs/de */
|
|
HDMI_WriteI2C_Byte(0x51, 0x42);
|
|
|
|
/* Embedded sync mode input enable. */
|
|
HDMI_WriteI2C_Byte(0x48, 0x08);
|
|
#else
|
|
/* _External_sync_ */
|
|
/* Select TTL process module input video data */
|
|
HDMI_WriteI2C_Byte(0x51, 0x00);
|
|
HDMI_WriteI2C_Byte(0x48, 0x00);
|
|
#endif
|
|
}
|
|
|
|
static void LT8618SXB_PLL_setting(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x81);
|
|
HDMI_WriteI2C_Byte(0x23, 0x40);
|
|
/* icp set */
|
|
HDMI_WriteI2C_Byte(0x24, 0x64);
|
|
HDMI_WriteI2C_Byte(0x2e, 0x01);
|
|
HDMI_WriteI2C_Byte(0x2f, 0x10);
|
|
HDMI_WriteI2C_Byte(0x26, 0x55);
|
|
|
|
/* Parameters required by LT8618SXB_PLL_setting( void ) */
|
|
CLK_bound = Format_Timing[Resolution_Num][Clk_bound_SDR + (u8)(Use_DDRCLK)];
|
|
|
|
if (Use_DDRCLK) {
|
|
HDMI_WriteI2C_Byte(0x4d, 0x05);
|
|
|
|
/* 0x05 pre-divider3 ddr 02 */
|
|
HDMI_WriteI2C_Byte(0x25, LT8618SXB_BT1120_DDR_PLL_setting[CLK_bound][0x00]);
|
|
/* 9e */
|
|
HDMI_WriteI2C_Byte(0x2c, LT8618SXB_BT1120_DDR_PLL_setting[CLK_bound][0x01]);
|
|
/* 88 */
|
|
HDMI_WriteI2C_Byte(0x2d, LT8618SXB_BT1120_DDR_PLL_setting[CLK_bound][0x02]);
|
|
/* 0x60 ddr 0x66 */
|
|
HDMI_WriteI2C_Byte(0x27, 0x66);
|
|
/* 0x88 */
|
|
HDMI_WriteI2C_Byte(0x28, 0x88);
|
|
} else {
|
|
HDMI_WriteI2C_Byte(0x4d, 0x00);
|
|
|
|
/* 0x05 pre-divider3 ddr 02 */
|
|
HDMI_WriteI2C_Byte(0x25, LT8618SXB_BT1120_SDR_PLL_setting[CLK_bound][0x00]);
|
|
/* 9e */
|
|
HDMI_WriteI2C_Byte(0x2c, LT8618SXB_BT1120_SDR_PLL_setting[CLK_bound][0x01]);
|
|
/* 88 */
|
|
HDMI_WriteI2C_Byte(0x2d, LT8618SXB_BT1120_SDR_PLL_setting[CLK_bound][0x02]);
|
|
/* 0x60 ddr 0x66 */
|
|
HDMI_WriteI2C_Byte(0x27, 0x06);
|
|
/* 0x88 */
|
|
HDMI_WriteI2C_Byte(0x28, 0x00);
|
|
}
|
|
|
|
/*
|
|
* as long as changing the resolution or changing the input clock,
|
|
* You need to configure the following registers.
|
|
*/
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
HDMI_WriteI2C_Byte(0xde, 0x00);
|
|
HDMI_WriteI2C_Byte(0xde, 0xc0);
|
|
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
HDMI_WriteI2C_Byte(0x16, 0xf1);
|
|
HDMI_WriteI2C_Byte(0x16, 0xf3);
|
|
}
|
|
|
|
static void LT8618SXB_Audio_setting(void)
|
|
{
|
|
/* IIS Input */
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
/* bit7 = 0 : DVI output; bit7 = 1: HDMI output */
|
|
HDMI_WriteI2C_Byte(0xd6, 0x8e);
|
|
/* sync polarity */
|
|
HDMI_WriteI2C_Byte(0xd7, 0x04);
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x84);
|
|
HDMI_WriteI2C_Byte(0x06, 0x08);
|
|
/* SD0 channel selected */
|
|
HDMI_WriteI2C_Byte(0x07, 0x10);
|
|
/* 0x00 :Left justified; default */
|
|
HDMI_WriteI2C_Byte(0x09, 0x00);
|
|
/* 0x02 :Right justified; */
|
|
HDMI_WriteI2C_Byte(0x0f, 0x0b + Sample_Freq[_48KHz]);
|
|
/* CTS_N / 2; 32bit */
|
|
HDMI_WriteI2C_Byte(0x34, 0xd4);
|
|
/* CTS_N / 4; 16bit */
|
|
HDMI_WriteI2C_Byte(0x35, (u8)(IIS_N[_48KHz] / 0x10000));
|
|
HDMI_WriteI2C_Byte(0x36, (u8)((IIS_N[_48KHz] & 0x00FFFF) / 0x100));
|
|
HDMI_WriteI2C_Byte(0x37, (u8)(IIS_N[_48KHz] & 0x0000FF));
|
|
/* Null packet enable */
|
|
HDMI_WriteI2C_Byte(0x3c, 0x21);
|
|
}
|
|
|
|
static void LT8618SXB_CSC_setting(void)
|
|
{
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
|
|
/* No csc */
|
|
HDMI_WriteI2C_Byte(0xb9, 0x00);
|
|
}
|
|
|
|
static void LT8618SXB_AVI_setting(void)
|
|
{
|
|
u8 AVI_PB0;
|
|
u8 AVI_PB1;
|
|
u8 AVI_PB2;
|
|
|
|
/* PB1,color space: YUV444 0x70;YUV422 0x30; RGB 0x10 */
|
|
AVI_PB1 = 0x30;
|
|
|
|
/* PB2; picture aspect rate: 0x19:4:3 ; 0x2A : 16:9 */
|
|
AVI_PB2 = Format_Timing[Resolution_Num][Pic_Ratio];
|
|
AVI_PB0 = ((AVI_PB1 + AVI_PB2 + VIC_Num) <= 0x6f) ? (0x6f - AVI_PB1 - AVI_PB2 - VIC_Num) :
|
|
(0x16f - AVI_PB1 - AVI_PB2 - VIC_Num);
|
|
/* register bank */
|
|
HDMI_WriteI2C_Byte(0xff, 0x84);
|
|
/* PB0,avi packet checksum */
|
|
HDMI_WriteI2C_Byte(0x43, AVI_PB0);
|
|
/* PB1,color space: YUV444 0x70;YUV422 0x30; RGB 0x10 */
|
|
HDMI_WriteI2C_Byte(0x44, AVI_PB1);
|
|
/* PB2;picture aspect rate: 0x19:4:3 ; 0x2A : 16:9 */
|
|
HDMI_WriteI2C_Byte(0x45, AVI_PB2);
|
|
/* PB4;vic ,0x10: 1080P ; 0x04 : 720P */
|
|
HDMI_WriteI2C_Byte(0x47, VIC_Num);
|
|
/* data iland */
|
|
HDMI_WriteI2C_Byte(0x10, 0x2c);
|
|
/* act_h_blank */
|
|
HDMI_WriteI2C_Byte(0x12, 0x64);
|
|
if (VIC_Num == 95) {
|
|
/* UD1 infoframe enable */
|
|
HDMI_WriteI2C_Byte(0x3d, 0x2a);
|
|
HDMI_WriteI2C_Byte(0x74, 0x81);
|
|
HDMI_WriteI2C_Byte(0x75, 0x01);
|
|
HDMI_WriteI2C_Byte(0x76, 0x05);
|
|
HDMI_WriteI2C_Byte(0x77, 0x49);
|
|
HDMI_WriteI2C_Byte(0x78, 0x03);
|
|
HDMI_WriteI2C_Byte(0x79, 0x0c);
|
|
HDMI_WriteI2C_Byte(0x7a, 0x00);
|
|
HDMI_WriteI2C_Byte(0x7b, 0x20);
|
|
HDMI_WriteI2C_Byte(0x7c, 0x01);
|
|
} else {
|
|
/*
|
|
* UD1 infoframe disable
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x3d, 0x0a);
|
|
}
|
|
}
|
|
|
|
static void LT8618SXB_TX_Phy(void)
|
|
{
|
|
/*
|
|
* register bank
|
|
*/
|
|
HDMI_WriteI2C_Byte(0xff, 0x81);
|
|
HDMI_WriteI2C_Byte(0x30, 0xea);
|
|
HDMI_WriteI2C_Byte(0x31, 0x44);
|
|
HDMI_WriteI2C_Byte(0x32, 0x4a);
|
|
HDMI_WriteI2C_Byte(0x33, 0x0b);
|
|
HDMI_WriteI2C_Byte(0x34, 0x00);
|
|
HDMI_WriteI2C_Byte(0x35, 0x00);
|
|
HDMI_WriteI2C_Byte(0x36, 0x00);
|
|
HDMI_WriteI2C_Byte(0x37, 0x44);
|
|
HDMI_WriteI2C_Byte(0x3f, 0x0f);
|
|
/*
|
|
* 0xa0 -- CLK tap0 swing
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x40, 0xa0);
|
|
/*
|
|
* 0xa0 -- D0 tap0 swing
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x41, 0xa0);
|
|
/*
|
|
* 0xa0 -- D1 tap0 swing
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x42, 0xa0);
|
|
/*
|
|
* 0xa0 -- D2 tap0 swing
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x43, 0xa0);
|
|
HDMI_WriteI2C_Byte(0x44, 0x0a);
|
|
}
|
|
|
|
#ifdef _Embedded_sync_
|
|
static void LT8618SXB_BT_Timing_setting(void)
|
|
{
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
HDMI_WriteI2C_Byte(0x20, (u8)(Format_Timing[Resolution_Num][H_act] / 256));
|
|
HDMI_WriteI2C_Byte(0x21, (u8)(Format_Timing[Resolution_Num][H_act] % 256));
|
|
HDMI_WriteI2C_Byte(0x22, (u8)(Format_Timing[Resolution_Num][H_fp] / 256));
|
|
HDMI_WriteI2C_Byte(0x23, (u8)(Format_Timing[Resolution_Num][H_fp] % 256));
|
|
HDMI_WriteI2C_Byte(0x24, (u8)(Format_Timing[Resolution_Num][H_sync] / 256));
|
|
HDMI_WriteI2C_Byte(0x25, (u8)(Format_Timing[Resolution_Num][H_sync] % 256));
|
|
HDMI_WriteI2C_Byte(0x26, 0x00);
|
|
HDMI_WriteI2C_Byte(0x27, 0x00);
|
|
HDMI_WriteI2C_Byte(0x36, (u8)(Format_Timing[Resolution_Num][V_act] / 256));
|
|
HDMI_WriteI2C_Byte(0x37, (u8)(Format_Timing[Resolution_Num][V_act] % 256));
|
|
HDMI_WriteI2C_Byte(0x38, (u8)(Format_Timing[Resolution_Num][V_fp] / 256));
|
|
HDMI_WriteI2C_Byte(0x39, (u8)(Format_Timing[Resolution_Num][V_fp] % 256));
|
|
HDMI_WriteI2C_Byte(0x3a, (u8)(Format_Timing[Resolution_Num][V_bp] / 256));
|
|
HDMI_WriteI2C_Byte(0x3b, (u8)(Format_Timing[Resolution_Num][V_bp] % 256));
|
|
HDMI_WriteI2C_Byte(0x3c, (u8)(Format_Timing[Resolution_Num][V_sync] / 256));
|
|
HDMI_WriteI2C_Byte(0x3d, (u8)(Format_Timing[Resolution_Num][V_sync] % 256));
|
|
}
|
|
|
|
static void LT8618SXB_Phase_config(void)
|
|
{
|
|
u8 Temp = 0x00;
|
|
u8 OK_CNT = 0x00;
|
|
u8 Jump_CNT = 0x00;
|
|
u8 Jump_Num = 0x00;
|
|
bool b_OK = 0;
|
|
u16 v_active = 0x0000;
|
|
u16 h_active = 0x0000;
|
|
u16 h_total = 0x0000;
|
|
/*
|
|
* register bank
|
|
*/
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
HDMI_WriteI2C_Byte(0x13, 0xf1);
|
|
mdelay(5);
|
|
/*
|
|
* Reset TTL video process
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x13, 0xf9);
|
|
mdelay(100);
|
|
while (Temp <= 0x09) {
|
|
HDMI_WriteI2C_Byte(0xff, 0x81);
|
|
HDMI_WriteI2C_Byte(0x27, (0x60 + Temp));
|
|
HDMI_WriteI2C_Byte(0x4d, 0x0d);
|
|
/*
|
|
* > 30ms
|
|
*/
|
|
mdelay(50);
|
|
HDMI_WriteI2C_Byte(0xff, 0x82);
|
|
h_total = HDMI_ReadI2C_Byte(0x8f);
|
|
h_total = (h_total << 8) + HDMI_ReadI2C_Byte(0x90);
|
|
|
|
v_active = HDMI_ReadI2C_Byte(0x8b);
|
|
v_active = (v_active << 8) + HDMI_ReadI2C_Byte(0x8c);
|
|
|
|
h_active = HDMI_ReadI2C_Byte(0x8d);
|
|
h_active = (h_active << 8) + HDMI_ReadI2C_Byte(0x8e);
|
|
|
|
if ((v_active > (Format_Timing[Resolution_Num][V_act] - 5)) &&
|
|
(v_active < (Format_Timing[Resolution_Num][V_act] + 5)) &&
|
|
(h_active > (Format_Timing[Resolution_Num][H_act] - 5)) &&
|
|
(h_active < (Format_Timing[Resolution_Num][H_act] + 5)) &&
|
|
(h_total > (Format_Timing[Resolution_Num][H_tol] - 5)) &&
|
|
(h_total < (Format_Timing[Resolution_Num][H_tol] + 5))) {
|
|
OK_CNT++;
|
|
|
|
if (b_OK == 0) {
|
|
b_OK = 1;
|
|
Jump_CNT++;
|
|
if ((Jump_CNT == 1) || (Jump_CNT == 3)) {
|
|
Jump_Num = Temp;
|
|
}
|
|
}
|
|
} else {
|
|
if (b_OK) {
|
|
b_OK = 0;
|
|
Jump_CNT++;
|
|
}
|
|
}
|
|
|
|
Temp++;
|
|
}
|
|
|
|
HDMI_WriteI2C_Byte(0xff, 0x81);
|
|
|
|
if ((Jump_CNT == 1) || (Jump_CNT == 2) || (Jump_CNT == 4)) {
|
|
HDMI_WriteI2C_Byte(0x27, (0x60 + Jump_Num + (OK_CNT / 2)));
|
|
} else if (Jump_CNT == 3) {
|
|
HDMI_WriteI2C_Byte(0x27, (0x60 + (Jump_Num + (OK_CNT / 2)) % 0x0a));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static bool lt_check_module_param(void)
|
|
{
|
|
if (norm >= LT_OUT_BUTT) {
|
|
printk("module param norm(%u) invalid, it should be in [%u,%u]\n", norm, 0, LT_OUT_USER);
|
|
return -1;
|
|
}
|
|
|
|
if ((Use_DDRCLK != LT_CLK_EDGE_SINGLE) && (Use_DDRCLK != LT_CLK_EDGE_DUAL)) {
|
|
printk("module param Use_DDRCLK(%u) invalid, it should be 0 or 1\n", Use_DDRCLK);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __init LT8618SX_Initial(void)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
if (lt_check_module_param() != 0) {
|
|
return -1;
|
|
}
|
|
|
|
I2CADR = _LT8618SX_ADR;
|
|
|
|
ret = misc_register(<_dev);
|
|
if (ret) {
|
|
DPRINTK(0, "could not register lt8618sx device\n");
|
|
goto end0;
|
|
}
|
|
lt_reboot_init();
|
|
|
|
ret = i2c_client_init();
|
|
if (ret != 0) {
|
|
DPRINTK(0, "could not init i2c client\n");
|
|
goto end1;
|
|
}
|
|
LT8618SX_VideoSel(norm);
|
|
|
|
/*
|
|
* register bank
|
|
*/
|
|
HDMI_WriteI2C_Byte(0xff, 0x80);
|
|
HDMI_WriteI2C_Byte(0xee, 0x01);
|
|
|
|
/*
|
|
* reset MIPI Rx logic.
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x11, 0x00);
|
|
|
|
HDMI_WriteI2C_Byte(0x13, 0xf1);
|
|
/*
|
|
* Reset TTL video process
|
|
*/
|
|
HDMI_WriteI2C_Byte(0x13, 0xf9);
|
|
|
|
/*
|
|
* TTL mode
|
|
*/
|
|
LT8618SXB_TTL_Input_Analog();
|
|
|
|
LT8618SXB_TTL_Input_Digtal();
|
|
|
|
/*
|
|
* Wait for the signal to be stable and decide whether the delay is
|
|
* necessary according to the actual situation
|
|
*/
|
|
mdelay(1000);
|
|
|
|
/*
|
|
* For debug
|
|
*/
|
|
LT8618SXB_Video_check();
|
|
|
|
/*
|
|
* PLL
|
|
*/
|
|
LT8618SXB_PLL_setting();
|
|
LT8618SXB_Audio_setting();
|
|
LT8618SXB_CSC_setting();
|
|
#ifdef _LT8618_HDCP_
|
|
LT8618SXB_HDCP_Init();
|
|
#endif
|
|
LT8618SXB_AVI_setting();
|
|
LT8618SXB_TX_Phy();
|
|
/*
|
|
* This operation is not necessary. Read TV EDID if necessary.
|
|
* Read TV EDID
|
|
*/
|
|
LT8918SXB_Read_EDID();
|
|
#ifdef _LT8618_HDCP_
|
|
LT8618SXB_HDCP_Enable();
|
|
#endif
|
|
|
|
#ifdef _Embedded_sync_
|
|
LT8618SXB_BT_Timing_setting();
|
|
LT8618SXB_Phase_config();
|
|
#endif
|
|
|
|
printk("load lt8618sx.ko ....OK!\n");
|
|
return 0;
|
|
end1:
|
|
lt_reboot_exit();
|
|
misc_deregister(<_dev);
|
|
end0:
|
|
printk("load lt8618sx.ko ....failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* When the lt8618sxb works, the resolution of the bt1120 signal changes.
|
|
* The following settings need to be configured.
|
|
*/
|
|
void Resolution_change(unsigned int Resolution)
|
|
{
|
|
/*
|
|
* Parameters required by LT8618SXB_BT_Timing_setting( void )
|
|
*/
|
|
Resolution_Num = Resolution;
|
|
|
|
/*
|
|
* Parameters required by LT8618SXB_PLL_setting( void )
|
|
*/
|
|
CLK_bound = Format_Timing[Resolution_Num][Clk_bound_SDR + (u8)(Use_DDRCLK)];
|
|
VIC_Num = Format_Timing[Resolution_Num][Vic];
|
|
LT8618SXB_PLL_setting();
|
|
LT8618SXB_AVI_setting();
|
|
#ifdef _Embedded_sync_
|
|
LT8618SXB_BT_Timing_setting();
|
|
LT8618SXB_Phase_config();
|
|
#endif
|
|
}
|
|
|
|
static void __exit LT8618SX_DeInitial(void)
|
|
{
|
|
lt_reset_ttl();
|
|
i2c_client_exit();
|
|
lt_reboot_exit();
|
|
misc_deregister(<_dev);
|
|
printk("unload lt8618sx.ko ....OK!\n");
|
|
}
|
|
|
|
module_init(LT8618SX_Initial);
|
|
module_exit(LT8618SX_DeInitial);
|
|
|
|
MODULE_LICENSE("GPL");
|