龙讯LT9211是一个高性能转换器,支持MIPI LVDS TTL两两之间转换。
使用此款芯片大部分为MIPI与LVDS进行互相转换。
下图为LT9211的典型应用图:
(1)insmod加载ko文件后,ls /dev 查看是否生成新的video节点。
->如果没有生成video节点,查看ko是否加载失败,dts配置是否正确。
(2)查看设备是否成功挂载在IIC上
->i2cdetect -r -y 5 (lt9211在iic1上地址为ox2b)。
(3)查看LT9211_ChipID寄存器值
void LT9211_ChipID(void)
{
lt8918_write_reg(0xff,0x81);//register bank
pr_info("\r\nLT9211 Chip ID:%x,",lt8918_read_reg(0x00));//18
pr_info("%x, ",lt8918_read_reg(0x01));//1
pr_info("%x, ",lt8918_read_reg(0x02));//e4
}
(4)检查LT9211打印log
hfp,hbp,hsync,vfp,vbp,vysnc输出正确。
(5)测量mipi信号
MIPI data :1.8V的方波。
图像数据在soc mipi接收过程是需要检测到各个lane的LP11-LP10-LP00的一个状态切换后才会切换
到高速模式准备接收。
(6)确认mipi lane数与硬件连接的是否一致。
/******************************************************************************
* @project: LT9211
* @file: lt9211.c
* @author: zll
* @company: LONTIUM COPYRIGHT and CONFIDENTIAL
* @date: 2019.04.10
/******************************************************************************/
#include "include.h"
u16 hact, vact;
u16 hs, vs;
u16 hbp, vbp;
u16 htotal, vtotal;
u16 hfp, vfp;
u8 VideoFormat=0;
u32 lvds_clk_in = 0;
#define OFFSET 5
struct video_timing *pVideo_Format;
//hfp, hs, hbp, hact, htotal, vfp, vs, vbp, vact, vtotal, pixclk
struct video_timing video_640x480_60Hz ={ 8, 96, 40, 640, 800, 33, 2, 10, 480, 525, 25000};
struct video_timing video_720x480_60Hz ={16, 62, 60, 720, 858, 9, 6, 30, 480, 525, 27000};
struct video_timing video_1280x720_60Hz ={110,40, 220,1280, 1650, 5, 5, 20, 720, 750, 74250};
struct video_timing video_1280x720_30Hz ={110,40, 220,1280, 1650, 5, 5, 20, 720, 750, 74250};
struct video_timing video_1366x768_60Hz ={26, 110,110,1366, 1592, 13, 6, 13, 768, 800, 81000};
struct video_timing video_1920x720_60Hz ={148,44, 88, 1920, 2200, 28, 5, 12, 720, 765, 88000};
struct video_timing video_1920x1080_30Hz ={88, 44, 148,1920, 2200, 4, 5, 36, 1080, 1125, 74250};
struct video_timing video_1920x1080_60Hz ={88, 44, 148,1920, 2200, 4, 5, 36, 1080, 1125, 148500};
struct video_timing video_1920x1200_60Hz ={48, 32, 80,1920, 2080, 3, 6, 26, 1200, 1235, 154000};
struct video_timing video_3840x2160_30Hz ={176,88, 296,3840, 4400, 8, 10, 72, 2160, 2250, 297000};
void LT9211_ChipID(void)
{
HDMI_WriteI2C_Byte(0xff,0x81);//register bank
print("\r\nLT9211 Chip ID:%x,",HDMI_ReadI2C_Byte(0x00));
print("%x,",HDMI_ReadI2C_Byte(0x01));
print("%x",HDMI_ReadI2C_Byte(0x02));
}
/** video chk soft rst **/
void lt9211_vid_chk_rst(void)
{
HDMI_WriteI2C_Byte(0xff,0x81);
HDMI_WriteI2C_Byte(0x10,0xbe);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x10,0xfe);
}
/** lvds rx logic rst **/
void lt9211_lvdsrx_logic_rst(void)
{
HDMI_WriteI2C_Byte(0xff,0x81);
HDMI_WriteI2C_Byte(0x0c,0xeb);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x0c,0xfb);
}
void LT9211_SystemInt(void)
{
/* system clock init */
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x01,0x18);
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x06,0x61);
HDMI_WriteI2C_Byte(0x07,0xa8); //fm for sys_clk
HDMI_WriteI2C_Byte(0xff,0x87); // ? txpll ? б ? ?
HDMI_WriteI2C_Byte(0x14,0x08); //default value
HDMI_WriteI2C_Byte(0x15,0x00); //default value
HDMI_WriteI2C_Byte(0x18,0x0f);
HDMI_WriteI2C_Byte(0x22,0x08); //default value
HDMI_WriteI2C_Byte(0x23,0x00); //default value
HDMI_WriteI2C_Byte(0x26,0x0f);
}
void LT9211_LvdsRxPhy(void)
{
#ifdef INPUT_PORTA
print("\r\nPort A PHY Config");
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x02,0x8B); //Port A LVDS mode enable
HDMI_WriteI2C_Byte(0x05,0x21); //port A CLK lane swap
HDMI_WriteI2C_Byte(0x07,0x1f); //port A clk enable
HDMI_WriteI2C_Byte(0x04,0xa0); //select port A clk as byteclk
//HDMI_WriteI2C_Byte(0x09,0xFC); //port A P/N swap
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x33,0xe4); //Port A Lane swap
#endif
#ifdef INPUT_PORTB
print("\r\nPort B PHY Config");
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x02,0x88); //Port A/B LVDS mode enable
HDMI_WriteI2C_Byte(0x05,0x21); //port A CLK lane swap and rterm turn-off
HDMI_WriteI2C_Byte(0x0d,0x21); //port B CLK lane swap
HDMI_WriteI2C_Byte(0x07,0x1f); //port A clk enable (? Portb?,porta lane0 clk? )
HDMI_WriteI2C_Byte(0x0f,0x1f); //port B clk enable
//HDMI_WriteI2C_Byte(0x10,0x00); //select port B clk as byteclk
HDMI_WriteI2C_Byte(0x04,0xa1); //reserve
//HDMI_WriteI2C_Byte(0x11,0x01); //port B P/N swap
HDMI_WriteI2C_Byte(0x10,0xfc);
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x34,0xe4); //Port B Lane swap
HDMI_WriteI2C_Byte(0xff,0xd8);
HDMI_WriteI2C_Byte(0x16,0x80);
#endif
HDMI_WriteI2C_Byte(0xff,0x81);
HDMI_WriteI2C_Byte(0x20,0x7f);
HDMI_WriteI2C_Byte(0x20,0xff); //mlrx calib reset
}
void LT9211_LvdsRxDigital(void)
{
HDMI_WriteI2C_Byte(0xff,0x85);
HDMI_WriteI2C_Byte(0x88,0x10); //LVDS input, MIPI output
HDMI_WriteI2C_Byte(0xff,0xd8);
if(INPUT_PORT_NUM == 1) //1Port LVDS Input
{
HDMI_WriteI2C_Byte(0x10,0x80);
print("\r\nLVDS Port Num: 1");
}
else if(INPUT_PORT_NUM == 2) //2Port LVDS Input
{
HDMI_WriteI2C_Byte(0x10,0x00);
print("\r\nLVDS Port Num: 2");
}
else
{
print("\r\nPort Num Set Error");
}
lt9211_vid_chk_rst(); //video chk soft rst
lt9211_lvdsrx_logic_rst(); //lvds rx logic rst
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x30,0x45); //port AB input port sel
if(LVDS_FORMAT == JEDIA_FORMAT)
{
HDMI_WriteI2C_Byte(0xff,0x85);
HDMI_WriteI2C_Byte(0x59,0xd0);
HDMI_WriteI2C_Byte(0xff,0xd8);
HDMI_WriteI2C_Byte(0x11,0x40);
}
}
int lt9211_lvds_clkstb_check(void)
{
u8 porta_clk_state = 0;
u8 portb_clk_state = 0;
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x00,0x01);
Timer0_Delay1ms(300);
porta_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x00,0x02);
Timer0_Delay1ms(300);
portb_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));
if(INPUT_PORT_NUM == 1)
{
#ifdef INPUT_PORTA
if( porta_clk_state )
{
return 1;
}
else
{
return 0;
}
#endif
#ifdef INPUT_PORTB
if( portb_clk_state )
{
return 1;
}
else
{
return 0;
}
#endif
}
else if(INPUT_PORT_NUM == 2)
{
if(porta_clk_state && portb_clk_state)
{
return 1;
}
else
{
return 0;
}
}
}
void LT9211_ClockCheckDebug(void)
{
#ifdef _uart_debug_
u32 fm_value;
lvds_clk_in = 0;
#ifdef INPUT_PORTA
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x00,0x01);
Timer0_Delay1ms(50);
fm_value = 0;
fm_value = (HDMI_ReadI2C_Byte(0x08) &(0x0f));
fm_value = (fm_value<<8) ;
fm_value = fm_value + HDMI_ReadI2C_Byte(0x09);
fm_value = (fm_value<<8) ;
fm_value = fm_value + HDMI_ReadI2C_Byte(0x0a);
print("\r\nPort A lvds clock: ");
printdec_u32(fm_value);
lvds_clk_in = fm_value;
#endif
#ifdef INPUT_PORTB
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x00,0x02);
Timer0_Delay1ms(50);
fm_value = 0;
fm_value = (HDMI_ReadI2C_Byte(0x08) &(0x0f));
fm_value = (fm_value<<8) ;
fm_value = fm_value + HDMI_ReadI2C_Byte(0x09);
fm_value = (fm_value<<8) ;
fm_value = fm_value + HDMI_ReadI2C_Byte(0x0a);
print("\r\nPort B lvds clock: ");
printdec_u32(fm_value);
lvds_clk_in = fm_value;
#endif
#endif
}
void LT9211_LvdsRxPll(void)
{
u8 loopx = 0;
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x25,0x05);
HDMI_WriteI2C_Byte(0x27,0x02);
if(INPUT_PORT_NUM == 1) //1Port LVDS Input
{
HDMI_WriteI2C_Byte(0x24,0x24); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL 0x2c.
HDMI_WriteI2C_Byte(0x28,0x44); //0x64
}
else if(INPUT_PORT_NUM == 2) //2Port LVDS Input
{
HDMI_WriteI2C_Byte(0x24,0x2c); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL 0x2c.
HDMI_WriteI2C_Byte(0x28,0x64); //0x64
}
else
{
print("\r\n LvdsRxPll: lvds port count error");
}
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0xff,0x87);
HDMI_WriteI2C_Byte(0x05,0x00);
HDMI_WriteI2C_Byte(0x05,0x80);
Timer0_Delay1ms(100);
for(loopx = 0; loopx < 10; loopx++) //Check Rx PLL cal
{
HDMI_WriteI2C_Byte(0xff,0x87);
if(HDMI_ReadI2C_Byte(0x12)& 0x80)
{
if(HDMI_ReadI2C_Byte(0x11)& 0x80)
{
print("\r\nLT9211 rx cal done");
}
else
{
print("\r\nLT9211 rx cal undone!!");
}
print("\r\nLT9211 rx pll lock");
break;
}
else
{
print("\r\nLT9211 rx pll unlocked");
}
}
}
void LT9211_VideoCheck(void)
{
u8 sync_polarity;
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x20,0x00);
sync_polarity = HDMI_ReadI2C_Byte(0x70);
vs = HDMI_ReadI2C_Byte(0x71);
hs = HDMI_ReadI2C_Byte(0x72);
hs = (hs<<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);
vtotal = HDMI_ReadI2C_Byte(0x7A);
vtotal = (vtotal<<8) + HDMI_ReadI2C_Byte(0x7B);
htotal = HDMI_ReadI2C_Byte(0x7C);
htotal = (htotal<<8) + HDMI_ReadI2C_Byte(0x7D);
vact = HDMI_ReadI2C_Byte(0x7E);
vact = (vact<<8)+ HDMI_ReadI2C_Byte(0x7F);
hact = HDMI_ReadI2C_Byte(0x80);
hact = (hact<<8) + HDMI_ReadI2C_Byte(0x81);
print("\r\nsync_polarity = %x", sync_polarity);
if(!(sync_polarity & 0x01)) //hsync
{
HDMI_WriteI2C_Byte(0xff,0xd8);
HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10)| 0x10));
}
if(!(sync_polarity & 0x02)) //vsync
{
HDMI_WriteI2C_Byte(0xff,0xd8);
HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10)| 0x20));
}
print("\r\nhfp, hs, hbp, hact, htotal = ");
printdec_u32(hfp);
printdec_u32(hs);
printdec_u32(hbp);
printdec_u32(hact);
printdec_u32(htotal);
print("\r\nvfp, vs, vbp, vact, vtotal = ");
printdec_u32(vfp);
printdec_u32(vs);
printdec_u32(vbp);
printdec_u32(vact);
printdec_u32(vtotal);
if ((hact == video_1280x720_60Hz.hact ) &&( vact == video_1280x720_60Hz.vact ))
{
print("\r\nvideo_1280x720_60Hz");
pVideo_Format = &video_1280x720_60Hz;
}
else if ((hact == video_1920x1080_60Hz.hact ) &&( vact == video_1920x1080_60Hz.vact ))
{
print("\r\nvideo_1920x1080_60Hz");
pVideo_Format = &video_1920x1080_60Hz;
}
else
{
pVideo_Format = NULL;
print("\r\nvideo_none");
}
}
void LT9211_MipiTxpll(void)
{
u8 loopx;
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x36,0x03); //b7:txpll_pd
HDMI_WriteI2C_Byte(0x37,0x28);
HDMI_WriteI2C_Byte(0x38,0x44);
HDMI_WriteI2C_Byte(0x3a,0x93);
HDMI_WriteI2C_Byte(0xff,0x87);
HDMI_WriteI2C_Byte(0x13,0x00);
HDMI_WriteI2C_Byte(0x13,0x80);
Timer0_Delay1ms(100);
for(loopx = 0; loopx < 10; loopx++) //Check Tx PLL cal done
{
HDMI_WriteI2C_Byte(0xff,0x87);
if(HDMI_ReadI2C_Byte(0x1f)& 0x80)
{
if(HDMI_ReadI2C_Byte(0x20)& 0x80)
{
print("\r\nLT9211 tx pll lock");
}
else
{
print("\r\nLT9211 tx pll unlocked");
}
print("\r\nLT9211 tx pll cal done");
break;
}
else
{
print("\r\nLT9211 tx pll unlocked");
}
}
}
void LT9211_MipiTxPhy(void)
{
HDMI_WriteI2C_Byte(0xff,0x82);
HDMI_WriteI2C_Byte(0x62,0x00); //ttl output disable
HDMI_WriteI2C_Byte(0x3b,0x32); //mipi en
HDMI_WriteI2C_Byte(0xff,0x81);
HDMI_WriteI2C_Byte(0x20,0xfb);
Timer0_Delay1ms(10);
HDMI_WriteI2C_Byte(0x20,0xff); //tx rterm calibration
//HDMI_WriteI2C_Byte(0x48,0x5f); //Port A Lane P/N Swap
//HDMI_WriteI2C_Byte(0x49,0x92);
//HDMI_WriteI2C_Byte(0x52,0x5f); //Port B Lane P/N Swap
//HDMI_WriteI2C_Byte(0x53,0x92);
HDMI_WriteI2C_Byte(0xff,0x86);
HDMI_WriteI2C_Byte(0x40,0x80); //tx_src_sel
/*port src sel*/
HDMI_WriteI2C_Byte(0x41,0x01);
HDMI_WriteI2C_Byte(0x42,0x23);
HDMI_WriteI2C_Byte(0x43,0x40); //Port A MIPI Lane Swap
HDMI_WriteI2C_Byte(0x44,0x12);
HDMI_WriteI2C_Byte(0x45,0x34); //Port B MIPI Lane Swap
}
void LT9211_MipiTxDigital(void)
{
HDMI_WriteI2C_Byte(0xff,0xd4);
HDMI_WriteI2C_Byte(0x1c,0x30); //hs_rqst_pre
HDMI_WriteI2C_Byte(0x1d,0x0a); //lpx
HDMI_WriteI2C_Byte(0x1e,0x06); //prpr
HDMI_WriteI2C_Byte(0x1f,0x0a); //trail
HDMI_WriteI2C_Byte(0x21,0x00); //[5]byte_swap,[0]burst_clk
HDMI_WriteI2C_Byte(0xff,0xd4);
HDMI_WriteI2C_Byte(0x12,0x1E);
HDMI_WriteI2C_Byte(0x15,0x01);
HDMI_WriteI2C_Byte(0x16,0x55);
HDMI_WriteI2C_Byte(0x10,0x01);
HDMI_WriteI2C_Byte(0x11,0x50); //read byteclk
HDMI_WriteI2C_Byte(0x13,0x0f); //bit[5:4]:lane num, bit[2]:bllp,bit[1:0]:vid_mode
HDMI_WriteI2C_Byte(0x14,0x04); //bit[5:4]:data typ,bit[2:0]:fmt sel 000:rgb888
HDMI_WriteI2C_Byte(0x21,0x02);
HDMI_WriteI2C_Byte(0xff,0xd0);
HDMI_WriteI2C_Byte(0x04,0x10);
}
void LT9211_SetTxTiming(void)
{
u16 hact, vact;
u16 hs, vs;
u16 hbp, vbp;
u16 htotal, vtotal;
u16 hfp, vfp;
hact = pVideo_Format->hact;
vact = pVideo_Format->vact;
htotal = pVideo_Format->htotal;
vtotal = pVideo_Format->vtotal;
hs = pVideo_Format->hs;
vs = pVideo_Format->vs;
hfp = pVideo_Format->hfp;
vfp = pVideo_Format->vfp;
hbp = pVideo_Format->hbp;
vbp = pVideo_Format->vbp;
HDMI_WriteI2C_Byte(0xff,0xd4);
HDMI_WriteI2C_Byte(0x04,0x01); //hs[7:0] not care
HDMI_WriteI2C_Byte(0x05,0x01); //hbp[7:0] not care
HDMI_WriteI2C_Byte(0x06,0x01); //hfp[7:0] not care
HDMI_WriteI2C_Byte(0x07,(u8)(hact>>8)); //hactive[15:8]
HDMI_WriteI2C_Byte(0x08,(u8)(hact)); //hactive[7:0]
HDMI_WriteI2C_Byte(0x09,(u8)(vs)+(u8)(vbp) - 1); //vfp[7:0]
HDMI_WriteI2C_Byte(0x0a,0x00); //bit[3:0]:vbp[11:8]
HDMI_WriteI2C_Byte(0x0b,0x01); //vbp[7:0]
HDMI_WriteI2C_Byte(0x0c,(u8)(vact>>8)); //vcat[15:8]
HDMI_WriteI2C_Byte(0x0d,(u8)(vact)); //vcat[7:0]
HDMI_WriteI2C_Byte(0x0e,0x00); //vfp[11:8]
HDMI_WriteI2C_Byte(0x0f,0x00); //vfp[7:0]
}
void LT9211_LVDS2MIPIDSI_Config(void)
{
print("\r\n*************LT9211 LVDS2MIPIDSI Config*************");
LT9211_ChipID();
LT9211_SystemInt();
LT9211_LvdsRxPhy();
LT9211_LvdsRxDigital();
LT9211_LvdsRxPll();
LT9211_ClockCheckDebug();
LT9211_VideoCheck();
if( pVideo_Format != NULL )
{
/********MIPI OUTPUT CONFIG********/
LT9211_MipiTxPhy();
LT9211_MipiTxpll();
LT9211_SetTxTiming();
LT9211_MipiTxDigital();
}
}
/******************************************************************************
* @project: LT9211
* @file: lt9211.h
* @author: zll
* @company: LONTIUM COPYRIGHT and CONFIDENTIAL
* @date: 2019.04.10
/******************************************************************************/
#ifndef _LT9211_H
#define _LT9211_H
/******************* LVDS Input Config ********************/
#define INPUT_PORTA
#define INPUT_PORTB
#define INPUT_PORT_NUM 2
typedef enum LVDS_FORMAT_ENUM{
VESA_FORMAT = 0,
JEDIA_FORMAT
};
#define LVDS_FORMAT VESA_FORMAT
typedef enum LVDS_MODE_ENUM{
DE_MODE = 0,
SYNC_MODE
};
#define LVDS_MODE SYNC_MODE
typedef struct video_timing{
u16 hfp;
u16 hs;
u16 hbp;
u16 hact;
u16 htotal;
u16 vfp;
u16 vs;
u16 vbp;
u16 vact;
u16 vtotal;
u32 pclk_khz;
};
void LT9211_LVDS2MIPIDSI_Config(void);
#endif