68411 是麦道微代理的一款hdmiout ic, 可以完成主芯片信源通道的扩展功能。
这里主芯片输出vb1 给68411, 然后68411 输出vb1 及一路hdmi out。该ic 也是一块由I2c控制的芯片但却是非标的i2c,所以我们需要对协议进行调整。
1. 总线配置
关于总线配置,可以参考"mstar 平台内核i2c总线介绍"
这篇博客。
2. 升级流程
novatek 升级流程,主要包含以下几个步骤:
一. ISP 模式设置;
二. 关闭写保护;
三. 擦除novatek 存储分区;
四. 写分区;
五. 校验分区;
六 . 开启写保护,退出ISP模式;
code 如下:
MAPI_BOOL mapi_i2c::doUpgradeNovaTek(const char* filePath)
{
MAPI_BOOL bRet = MAPI_FALSE;
MS_U16 u16Page = 0x000;
MAPI_U8 u8CurPage = 0x00;
MAPI_BOOL bSkipPage = MAPI_FALSE;
int nFileFd = open(NOVA_TEK_MASTER_DEV, O_RDWR);
if(nFileFd < 0)
{
return MAPI_FALSE;
}
bRet = NovaTekLoadFwBin(filePath);
if(!bRet)
{
goto END;
}
configIIcSpeed(nFileFd,50);
if(!NovaTekEnterIsp(nFileFd))
{
bRet = MAPI_FALSE;
close(nFileFd);
return bRet;
}
configIIcSpeed(nFileFd,100);
if(DualFwEnable == 1)
{
NovaTekEraseIDSector(nFileFd);
}
if(!NovaTekTErase(nFileFd))
{
bRet = MAPI_FALSE;
goto END;
}
configIIcSpeed(nFileFd,400);
MaxPages = Flash.Page;
if(DualFwEnable == 1)
{
CodePages = MaxPages/2 ;
FlashOffset = MaxPages/2 ;
}
else
{
CodePages = MaxPages;
FlashOffset = 0;
}
if (McuId == 0xa390 || McuId == 0xA400 || McuId == 0xA450)
{
bRet = NovaTek68390ProgramEnable(nFileFd, MAPI_TRUE);
}
for(u16Page = FlashOffset; (u16Page<(CodePages+FlashOffset))&&(u16Page<TOTAL_PAGE_SIZE); u16Page++)
{
u8CurPage = (u16Page - FlashOffset)%256;
bRet = NovaTekSetExntend(nFileFd,u16Page);
if(!bRet)
{
goto END;
}
else
{
g_current_bank = u16Page>>8;
}
SPageSum = 0;
bSkipPage = MAPI_TRUE;
for (int i = 0; i < 512; i++)
{
if(BinFilePageBuffer[u16Page-FlashOffset][i] != 0xff)
{
bSkipPage = MAPI_FALSE;
}
SPageSum += BinFilePageBuffer[u16Page-FlashOffset][i];
}
if (NovaTekCheckOverLap(u8CurPage))
{
bSkipPage = MAPI_TRUE;
}
if(!bSkipPage) //Bypass program?
{
bRet = NovaTekTProgram(nFileFd,u16Page);
if(!bRet)
{
goto END;
}
}
if(!bSkipPage)
{
bRet = NovaTekCmdGetCheckSum(nFileFd, u8CurPage);
if(!bRet)
{
goto END;
}
}
}
if (McuId == 0xa390 || McuId == 0xA400 || McuId == 0xA450)
{
bRet = NovaTek68390ProgramEnable(nFileFd, MAPI_FALSE);
}
if(DualFwEnable == 1)
{
if(!NovaTekUpdateIDSector(nFileFd))
{
bRet = MAPI_FALSE;
goto END;
}
}
END:
NovaTekBlockProtect(nFileFd,1);
configIIcSpeed(nFileFd,100);
NovaTekISPOff(nFileFd);
close(nFileFd);
if(!bRet)
{
printf("[%s][%d] novatek upgrade failed. \n", __FUNCTION__,__LINE__);
}
else
{
printf("[%s][%d] novatek upgrade successully. \n", __FUNCTION__,__LINE__);
}
return bRet;
}
2.1 升级文件加载
novatek 提供的bin 升级文件大小为1M,分成2048 个page,每个page大小为512 Bytes。
static MAPI_BOOL NovaTekLoadFwBin(const char* FwPath)
{
int nLen = 0;
FILE *pHandle = NULL;
char binFile[1024];
MS_U8 u8RSize = 0x00;
MS_U16 u16PageNo = 0x0000;
MS_U16 u16PageCnt = 0x0000;
MS_U32 u32TotalLen = 0x0000;
int nCurPosition = 0;
MAPI_BOOL bRet = MAPI_FALSE;
memset(binFile,0x00,sizeof(binFile));
memcpy(binFile,FwPath,strlen(FwPath));
pHandle = fopen(binFile, "r");
if(pHandle == NULL)
{
return MAPI_FALSE;
}
fseek(pHandle, 0, SEEK_END);
nLen = ftell(pHandle);
if(nLen != 1024*1024)
{
fclose(pHandle);
return MAPI_FALSE;
}
u16PageCnt = nLen/512;
// set to the file head
fseek(pHandle, 0, SEEK_SET);
for (u16PageNo = 0; u16PageNo < u16PageCnt; u16PageNo++)
{
u8RSize = fread(BinFilePageBuffer[u16PageNo], sizeof(char)*512, 1, pHandle);
u32TotalLen = u32TotalLen + u8RSize*512; // bytes
if(u8RSize <= 0)
{
bRet = MAPI_FALSE;
break;
}
}
nCurPosition = ftell(pHandle);
if(nCurPosition >= nLen)
{
bRet = MAPI_TRUE;
}
fclose(pHandle);
return bRet;
}
将升级文件存储在BinFilePageBuffer[2048][512] 的二维结构体中。
2.2 进入ISP mode
蓝色框框中的byte 序列就是发送给ic 的指令,发送完成之后,就可以通过reply 指令读取mcu id,本款芯片的mcu id 是0xA400。
static MAPI_BOOL NovaTekEnterIsp(int nDevFd)
{
MAPI_BOOL bStep1 = MAPI_FALSE;
MAPI_BOOL bStep2 = MAPI_FALSE;
MAPI_BOOL bStep3 = MAPI_FALSE;
MAPI_BOOL bStep4 = MAPI_FALSE;
MAPI_BOOL bStep5 = MAPI_FALSE;
NOVATEK_DG(printf("[%s][%d] Enter .\n", __FUNCTION__,__LINE__);)
do
{
bStep1 = NovaTekISPOn(nDevFd);
if(!bStep1)
{
printf("[%s][%d] novatek enter isp on mode failed. \n", __FUNCTION__,__LINE__);
break;
}
bStep2 = NovaTekSendSpecialCmd(nDevFd);
if(!bStep2)
{
printf("[%s][%d] send special cmd to novatek failed. \n", __FUNCTION__,__LINE__);
break;
}
bStep3 = NovaTekGetFlashId(nDevFd);
if(!bStep3)
{
printf("[%s][%d] novatek get flash id failed. \n", __FUNCTION__,__LINE__);
break;
}
bStep4 = NovaTekWpEnable(nDevFd);
if(!bStep4)
{
printf("[%s][%d] novatek wp enable failed. \n", __FUNCTION__,__LINE__);
break;
}
bStep5 = NovaTekBlockProtect(nDevFd,0);
if(!bStep5)
{
printf("[%s][%d] novatek block protect failed. \n", __FUNCTION__,__LINE__);
break;
}
}while(0);
return (bStep5 && bStep4 && bStep3 && bStep2 && bStep1);
}
NovaTekISPOn 是发送enter isp 的指令并通过reply 值校验mcu id。NovaTekSendSpecialCmd 发送一些特殊指令配置芯片烧写前的状态。NovaTekGetFlashId 读取flash id。
NovaTekWpEnable 设置novatek 模组wp 脚。NovaTekBlockProtect 关闭写保护。
2.3 擦除芯片
static MAPI_BOOL NovaTekTErase(int nDevFd)
{
MAPI_BOOL bRet = MAPI_FALSE;
MS_U32 BlkStart = 0x0000;
MS_U32 BlkEnd = 0x0000;
if (McuId == 0xa390 || McuId == 0xA400 || McuId == 0xA450)
{
bRet = NovaTek68390ProgramEnable(nDevFd, MAPI_TRUE);
}
{
bRet = NovaTekChipErase(nDevFd);
//6seconds M25P20 erase time max<6sec
//5seconds w25x40b erase time max<4sec
usleep(5*1000*1000);
bRet = NovaTekChipEraseReply(nDevFd);
if(!bRet)
{
return MAPI_FALSE;
}
}
if (McuId == 0xa390 || McuId == 0xA400 || McuId == 0xA450)
{
bRet = NovaTek68390ProgramEnable(nDevFd, MAPI_FALSE);
}
return bRet;
}
2.4 升级芯片
for(u16Page = FlashOffset; (u16Page<(CodePages+FlashOffset))&&(u16Page<TOTAL_PAGE_SIZE); u16Page++)
{
u8CurPage = (u16Page - FlashOffset)%256;
bRet = NovaTekSetExntend(nFileFd,u16Page);
if(!bRet)
{
printf("[%s][%d][error] set bank: 0x%x failed. \n", __FUNCTION__,__LINE__,u16Page>>8);
goto END;
}
else
{
g_current_bank = u16Page>>8;
}
printf("\n\n[%s][%d] start upgrade bank[%d], page[%d] \n",__FUNCTION__,__LINE__,g_current_bank,u8CurPage);
SPageSum = 0;
bSkipPage = MAPI_TRUE;
for (int i = 0; i < 512; i++)
{
if(BinFilePageBuffer[u16Page-FlashOffset][i] != 0xff)
{
bSkipPage = MAPI_FALSE;
}
SPageSum += BinFilePageBuffer[u16Page-FlashOffset][i];
}
if (NovaTekCheckOverLap(u8CurPage))
{
bSkipPage = MAPI_TRUE;
}
printf("\n[%s][%d] bSkipPage: %d, SPageSum: 0x%4x \n",__FUNCTION__,__LINE__,bSkipPage,SPageSum);
if(!bSkipPage) //Bypass program?
{
bRet = NovaTekTProgram(nFileFd,u16Page);
if(!bRet)
{
printf("[%s][%d][error] T Program failed\n", __FUNCTION__,__LINE__);
goto END;
}
}
printf("[%s][%d] bank: %d , Page : %d , SPageSum: 0x%4x .\n",__FUNCTION__,__LINE__,g_current_bank, u8CurPage,SPageSum);
if(!bSkipPage)
{
bRet = NovaTekCmdGetCheckSum(nFileFd, u8CurPage);
if(!bRet)
{
printf("[%s][%d][error] cmd get checksum failed\n", __FUNCTION__,__LINE__);
goto END;
}
}
printf("[%s][%d] upgrade bank[%d], page[%d] completed\n",__FUNCTION__,__LINE__,u16Page>>8,u8CurPage);
}
将前面保存bin 的二维数组数据写入分区中。NovaTekSetExntend 设置bank(每个bank 256 page), NovaTekTProgram 则是写page
static MAPI_BOOL NovaTekTProgram(int nDevFd, MS_U16 u16BufIndex)
{
MAPI_BOOL bRet = MAPI_FALSE;
MAPI_BOOL bRetReply = MAPI_FALSE;
bRet = NovaTekWritePgData(nDevFd, u16BufIndex);
usleep(200*1000);
bRetReply = NovaTekWritePgDataReply(nDevFd);
return (bRetReply&&bRet);
}
NovaTekCmdGetCheckSum 则是对每个写入的page 进行校验,通过则继续写下个page 的数据。
2.5 升级完成
升级完成之后,NovaTekBlockProtect打开数据保护,NovaTekISPOff 退出ISP 模式,进入用户模式。
3. 协议客制化
由于novatek 协议主要是ISP ON指令的交互时是非标的,另外一些功能指令交互时的时钟周期不一样。所以我们只需要对这个两部分进行修改即可,没有必要将整个i2c 协议修改。
3.1 i2c 总线周期设置
#define NOVA_TEK__IIC_ADDR (0x6E >> 1)
static MAPI_BOOL WriteBytesToDev(int nDevFd, MAPI_U8 u8Data[] , MAPI_U16 uDataLen)
{
int nRet = -1;
struct i2c_msg msg;
struct i2c_rdwr_ioctl_data ioctl_data = {0};
msg.addr = NOVA_TEK__IIC_ADDR;
msg.flags = I2C_SMBUS_WRITE; /* write */
msg.len = uDataLen;
msg.buf = u8Data;
ioctl_data.msgs = &msg;
ioctl_data.nmsgs = 1;
nRet = ioctl(nDevFd, I2C_RDWR, &ioctl_data);
if( nRet < 0)
{
return MAPI_FALSE;
}
return MAPI_TRUE;
}
static MAPI_BOOL configIIcSpeed(int nDevFd, MS_U16 u16Spd)
{
MS_U8 u8SRamPkg[4] = {0x88, 0x00, 0x00, 0x00};
u8SRamPkg[1] = u16Spd >> 8;
u8SRamPkg[2] = u16Spd & 0xFF;
u8SRamPkg[3] = (MS_U8)(0x6E^u8SRamPkg[0]^u8SRamPkg[1]^u8SRamPkg[2]);
if(WriteBytesToDev(nDevFd, u8SRamPkg, sizeof(u8SRamPkg)/sizeof(u8SRamPkg[0])) == MAPI_FALSE)
{
return MAPI_FALSE;
}
return MAPI_TRUE;
}
configIIcSpeed 通过 /dev/i2c-2 对应novatek 设备(0x6E)发送指令“0x6E 0x04 0x88 speekH speedL checkSum” 其中speedH 为i2c周期的高8位,speedL为i2c周期的低8位。
在kernel 的master_xfer 回调中会处理i2c 指令,mstar 平台则在vendor\mstar\kernel\linaro\mstar2\drv\iic\mdrv_iic_io.c中
g_i2c_speed 提取i2c 总线速率。
u16 getCustomerSpeed(void)
{
return g_i2c_speed;
}
3.2 ISP ON 指令客制化
int MDrv_SW_IIC_WriteBytesForCustomerIspEnable(u8 u8BusNum, u8 u8SlaveID, u16 u16size, u8* pu8Data, int stop)
{
int bRet = -EIO;
u16 u16Retries = g_IICBus[u8BusNum].u16Retries;
u16 u16size_temp=0;
u8* pu8Data_temp=(u8*)NULL;
SWI2C_DBG_FUNC();
if (u8BusAllocNum == 0)
return -ENOENT;
if (IIC_UseBusForCustomer(u8BusNum, getCustomerSpeed()) == FALSE)
{
return -ENOENT;
}
u16size_temp = u16size;
pu8Data_temp = (u8*)pu8Data;
while (u16size_temp)
{
u16size_temp--;
if ( IIC_Start(u8BusNum) == FALSE)
{
continue;
}
if (IIC_SendByte(u8BusNum, *pu8Data_temp) == FALSE) {
// goto fail;
}
// switch to next byte
pu8Data_temp++;
}
bRet = u16size - u16size_temp;
iic_delay(u8BusNum);
if (stop)
{
IIC_Stop(u8BusNum);
}
return bRet;
}
EXPORT_SYMBOL(MDrv_SW_IIC_WriteBytesForCustomerIspEnable);
至此,整个升级流程我介绍完成,更多指令客制化可以自己根据需求修改。 如果IO足够多也值可以使用IO口来模拟i2c, 这样会减小对原生协议干扰。
参考资料:
https://app.yinxiang.com/Home.action#n=88618994-1419-47ef-923f-79ecaf89d7d0&s=s30&ses=4&sh=5&sds=2&