/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include "lt8618sx.h" #include #include #include #include #include #include #include #include #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");