一、开发环境
平台:mt6833
主摄sensor: s5kjn1sq
模组厂:qtech
马达:dw9800
二、前期准备工作
- 在MOL上download 官方bring up文档,01_02_Driver_Check-PDAF_Driver_Porting_Guide_release_V1.4.pdf(最新).
- 问模组厂提供.ini文档,该文档记录了pdaf的相关参数,我们参考该文档配置sensor驱动
- OTP map,需要确认otp烧录了af和pdaf的数据
- 问sensor原厂提供pdaf PDMAP guide文档,该文档记录了PD Tail数据的存储格式,后续做pd buf的L,R分离时需要参考该文档
- pd_s5kjn1sqmipiraw.cpp文件,如果有现成的就问供应商拿现成的,如果没有则需要自己coding
三、前期需确认事项
1. 确认af是正常工作的,找相关bringup人员确认或在main log中搜索关键字"MoveLensTo"
2. 确认otp数据读取正常,日志搜索关键字"CamCalCamCal"
09-08 03:39:48.696 12049 31376 D CamCalCamCal: ver0923 ======================AWB CAM_CAL==================
09-08 03:39:48.696 12049 31376 D CamCalHelper: CamCalHelper Read data from memory[0]
09-08 03:39:48.696 12049 31413 D IspDrv_CAM: IspDrv_CAM[CQ_Allocate_method1] [0x0][21_0]:virtIspAddr:virt[0x79eb9e3000]/phy[0x2fcb90000]
09-08 03:39:48.696 12049 31376 D CamCalHelper: CamCalHelper Read data from memory[0]
09-08 03:39:48.696 12049 31414 D awb_mgr : [AWBInit()][AI NVRAM] AISAindex(0) revision(0) enable(0) , AIMAindex(0) revision(0) enable(0) [AWB NVRAM] index(1), SEGenable(0) SEGisothr(0) SEGcnt(0), E2Eenable(0) E2Eisothr(0) E2Ecnt(0)
09-08 03:39:48.696 12049 31376 D CamCalCamCal: ver0923 ======================AF CAM_CAL==================
09-08 03:39:48.696 12049 31376 D CamCalCamCal: ver0923 [AF] Inf=488 Marco=758
09-08 03:39:48.696 12049 31376 D CamCalCamCal: ver0923 ======================AF CAM_CAL==================
pdaf的数据我们需要看解析后的数据,porting的时候确认,需要确保pdaf是support状态,然后搜索关键字"parseStep3"
确保otp数据跟模组厂提供的ini文档中的参数应该是一致的。
3. 找sensor vendor确认pdaf type
4. 确认mirror filp
1.确认模组厂ini文档中烧录坐标点是否mirrorfilp
2. 确认preview(pdaf的使用场景)出图的mirrorflip
如果preview方向与模组厂烧录方向一致,则imgsensor_pd_info.iMirrorFlip = 0,如果preview方向为mirrorflip,模组厂烧录方向为normal,则imgsensor_pd_info.iMirrorFlip = 3,这块最好找供应商确认。
四、对应pd type走官方文档porting工作
五、debug问题
1.检索关键字"SensorModeSupport("
发现BufType为0(NOTDEF),需要走查代码vendor/mediatek/proprietary/hardware/mtkcam/aaa/source/isp_6s/af_assist_mgr/af_assist_mgr.cpp的AFAssisMgr::config实现:
看实际走了哪条else分支导致的BufType为0,
如果打印"Instance is not ready"表示pd_s5kjn1sqmipiraw.cpp中IsSupport函数返回值为false
如果打印"set PD calibration and tuning data fail"表示未成功设置好校准数据,可能是由于otp未导通
2. pd_s5kjn1sqmipiraw.cpp文件中ConvertPDBufFormat修改。当camsv收到sensor传输过来的tail数据后,经过ConvertPDBufFormat函数转换,生成一张pd L,R组成的raw图。
抓取转换后的图片如下:
转换后的图像上下应该是亮度一致,并且图案相同的。拍摄的pd图并不是跟原图案一样的,会有拉伸效果,因为pd的tail数据只是部分raw点,如上图,单看L的tail点,X方向是每2个像素取一个pd点,Y方向上是每8个像素取一个pd点,所以pd tail buf可看作是部分typical pixel组成的图,由于XY方向取点数量不同,会造成图像有拉伸的效果,这是正常的。
MUINT16* PD_S5KJN1SQMIPIRAW::ConvertPDBufFormat( MUINT32 /*i4Size*/, MUINT32 i4Stride, MUINT8 *ptrBufAddr, MUINT32 u4BitDepth, PD_AREA_T* /*ptrPDRegion*/)
{
if (m_eBufType == EPDBUF_VC) {
MUINT32 xSz = m_PDXSz ; // pixel
MUINT32 ySz = m_PDYSz ; // line
MUINT16 **ptr = NULL;
MUINT16 *ptrL = m_PDBuf;
MUINT16 *ptrR =m_PDBuf + m_PDBufSz/2 ;
MUINT32 xSzInByte = xSz * 10 / 8;
MUINT32 i, j;
AAA_LOGE("----------------------k19a------ConvertPDBufFormat------");
#if NEON_OPT
MUINT8 ucTbl0[5][8] = { { 4,0,1,2,3,5,6,7 },{ 0,1,6,2,3,4,5,7 },{ 0,1,2,3,4,5,6,7 },{ 1,2,3,4,0,5,6,7 },{ 0,1,3,4,5,6,2,7 } };
MUINT8 ucTbl1[2][8] = { { 0,9,10,19,0xFF,0xFF,0xFF,0xFF } ,{ 0xFF,0xFF,0xFF,0xFF,4,5,14,15 } };
MUINT8 ucTblShfit[16] = { 6,4,2,0, 6,4,2,0, 6,4,2,0, 6,4,2,0 };
uint8x8_t vu1x8Tbl0_0 = vld1_u8(ucTbl0[0]);
uint8x8_t vu1x8Tbl0_1 = vld1_u8(ucTbl0[1]);
uint8x8_t vu1x8Tbl0_3 = vld1_u8(ucTbl0[3]);
uint8x8_t vu1x8Tbl0_4 = vld1_u8(ucTbl0[4]);
uint8x8_t vu1x8Tbl1_0 = vld1_u8(ucTbl1[0]);
uint8x8_t vu1x8Tbl1_1 = vld1_u8(ucTbl1[1]);
uint8x16_t vu1x16TblShift = vld1q_u8(ucTblShfit);
#endif
for( j=0; j<ySz; j++)
{
MUINT32 idx = j * i4Stride;//Stride=width from raw file
if (j % 2 == 1)
ptr = &ptrL;
else
ptr = &ptrR;
i = 0;
#if NEON_OPT
unsigned char *ptr_src = (unsigned char *)(ptrBufAddr + idx);
uint8x8x3_t vu1x8x3Buf0;
uint8x8x2_t vu1x8x2Buf1, vu1x8x2TmpBuf;
uint8x8_t vu1x8LL, vu1x8LH, vu1x8HL, vu1x8HH, vu1x8LSB;
uint16x8_t vu2x8x1Buf3;
uint16x8_t vu2x8x1Buf4;
//uint16x8x2_t vu2x8x2Buf5;
uint8x16_t vu1x16Low2BitL, vu1x16Low2BitH;
for (; i <= xSzInByte - 40; i += 40, (*ptr) += 32, ptr_src += 40)
{
//if(j == 0)
// AAA_LOGD("ConvertPDBufFormat with neon");
//Step.1 Loading data from memory to register
vu1x8x3Buf0.val[0] = vld1_u8(ptr_src); //[M0 0, N0 1, M1 2, N1 3, D01 4, M2 5, N2 6, M3 7]
vu1x8x3Buf0.val[1] = vld1_u8(ptr_src + 8); //[N3 8, D23 9, M4 10, N4 11, M5 12, N5 13, D45 14, M6 15]
vu1x8x3Buf0.val[2] = vld1_u8(ptr_src + 16); //[N6 16, M7 17, N7 18, D67 19, M8 20, N8 21, M9 22, N9 23]
vu1x8x2Buf1.val[0] = vld1_u8(ptr_src + 24); //[D89 24, M10 25, N10 26, M11 27, N11 28, D1011 29, M12 30, N12 31]
vu1x8x2Buf1.val[1] = vld1_u8(ptr_src + 32); //[M13 32, N13 33, D1213 34, M14 35, N14 36, M15 37, N15 38, D1415 39]
vu1x8x3Buf0.val[0] = vtbl1_u8(vu1x8x3Buf0.val[0], vu1x8Tbl0_0); //[D01 4, 0, 1, 2, 3, 5, 6, 7]
vu1x8x3Buf0.val[1] = vtbl1_u8(vu1x8x3Buf0.val[1], vu1x8Tbl0_1); //[ 8,D23 9,D45 14, 10, 11, 12, 13, 15]
//vu1x8x3Buf0.val[2] [ 16, 17, 18,D67 19, 20, 21, 22, 23]
vu1x8x2Buf1.val[0] = vtbl1_u8(vu1x8x2Buf1.val[0], vu1x8Tbl0_3); //[ 25, 26, 27, 28,D89 24,D1011 29, 30, 31]
vu1x8x2Buf1.val[1] = vtbl1_u8(vu1x8x2Buf1.val[1], vu1x8Tbl0_4); //[ 32, 33, 35, 36, 37, 38,D1213 34,D1415 39]
//Step.2 Separate the upper 8-bit data
vu1x8LL = vext_u8(vu1x8x3Buf0.val[0], vu1x8x3Buf0.val[1], 1); //[M0 0,N0 1,M1 2,N1 3,M2 5,N2 6,M3 7,N3 8]
vu1x8LH = vext_u8(vu1x8x3Buf0.val[1], vu1x8x3Buf0.val[2], 3); //[M4 10,N4 11,M5 12,N5 13,M6 15,N6 16,M7 17,N7 18]
vu1x8HL = vext_u8(vu1x8x3Buf0.val[2], vu1x8x2Buf1.val[0], 4); //[M8 20,N8 21,M9 22,N9 23,M10 25,N10 26,M11 27,N11 28]
vu1x8HH = vext_u8(vu1x8x2Buf1.val[0], vu1x8x2Buf1.val[1], 6); //[M12 30,N12 31,M13 32,N13 33,M14 35,N14 36,M15 37,N15 38]
//Step.3 Separate the lower 2-bit data
// [4,9,14,19, 24,29,34,39] == [D01,D23,D45,D67,D89,D1011,D1213,D1415]
vu1x8LSB = vorr_u8(vtbl3_u8(vu1x8x3Buf0, vu1x8Tbl1_0), vtbl2_u8(vu1x8x2Buf1, vu1x8Tbl1_1));
// [D01,D23,D45,D67,D89,D1011,D1213,D1415] => val[0]: [D01,D01, D23,D23, D45,D45, D67,D67], val[1]: [D89,D89, D1011,D1011, D1213,D1213, D1415,D1415]
vu1x8x2Buf1 = vzip_u8(vu1x8LSB, vu1x8LSB);
// Low: [D01,D01, D23,D23, D45,D45, D67,D67]
// => val[0]: [D01,D01,D01,D01, D23,D23,D23,D23], val[1]: [D45,D45,D45,D45, D67,D67,D67,D67]
vu1x8x2TmpBuf = vzip_u8(vu1x8x2Buf1.val[0], vu1x8x2Buf1.val[0]);
// [(D01<<6)>>6, (D01<<4)>>6, (D01<<2)>>6, (D01<<0)>>6, (D23<<6)>>6 ,(D23<<4)>>6,(D23<<2)>>6,(D23<<0)>>6,
// (D45<<6)>>6, (D45<<4)>>6, (D45<<2)>>6, (D45<<0)>>6, (D67<<6)>>6 ,(D67<<4)>>6,(D67<<2)>>6,(D67<<0)>>6]
vu1x16Low2BitL = vshrq_n_u8(vshlq_u8(vcombine_u8(vu1x8x2TmpBuf.val[0], vu1x8x2TmpBuf.val[1]), vu1x16TblShift), 6);
// High: [D89,D89, D1011,D1011, D1213,D1213, D1415,D1415]
// Same as low
vu1x8x2TmpBuf = vzip_u8(vu1x8x2Buf1.val[1], vu1x8x2Buf1.val[1]);
vu1x16Low2BitH = vshrq_n_u8(vshlq_u8(vcombine_u8(vu1x8x2TmpBuf.val[0], vu1x8x2TmpBuf.val[1]), vu1x16TblShift), 6);
//Step.4 upper 8-bit data + lower 2-bit data
vu2x8x1Buf3 = vorrq_u16(vshlq_n_u16(vmovl_u8(vu1x8LL), 2), vmovl_u8(vget_low_u8(vu1x16Low2BitL))); //[M0,N0,M1,N1,M2,N2,M3,N3]
vu2x8x1Buf4 = vorrq_u16(vshlq_n_u16(vmovl_u8(vu1x8LH), 2), vmovl_u8(vget_high_u8(vu1x16Low2BitL))); //[M4,N4,M5,N5,M6,N6,M7,N7]
// unzip MNMNMNMN data => val[0]: [M0,M1,M2,M3,M4,M5,M6,M7] val[1]: [N0,N1,N2,N3,N4,N5,N6,N7]
//vu2x8x2Buf5 = vuzpq_u16(vu2x8x1Buf3, vu2x8x1Buf4);
vst1q_u16((*ptr), vu2x8x1Buf3);
vst1q_u16((*ptr) + 8, vu2x8x1Buf4);
vu2x8x1Buf3 = vorrq_u16(vshlq_n_u16(vmovl_u8(vu1x8HL), 2), vmovl_u8(vget_low_u8(vu1x16Low2BitH))); //[M8,N8,M9,N9,M10,N10,M11,N11]
vu2x8x1Buf4 = vorrq_u16(vshlq_n_u16(vmovl_u8(vu1x8HH), 2), vmovl_u8(vget_high_u8(vu1x16Low2BitH)));//[M12,N12,M13,N13,M14,N14,M15,N15]
// unzip MNMNMNMN data => val[0]: [M8,M9,M10,M11,M12,M13,M14,M15] val[1]: [N8,N9,N10,N11,N12,N13,N14,N15]
//vu2x8x2Buf5 = vuzpq_u16(vu2x8x1Buf3, vu2x8x1Buf4);
vst1q_u16((*ptr) + 16, vu2x8x1Buf3);
vst1q_u16((*ptr) + 24, vu2x8x1Buf4);
}
#endif
for(;i<xSzInByte;i+=5)
{
char val0 = ptrBufAddr[ idx + (i )];
char val1 = ptrBufAddr[ idx + (i+1)];
char val2 = ptrBufAddr[ idx + (i+2)];
char val3 = ptrBufAddr[ idx + (i+3)];
char val4 = ptrBufAddr[ idx + (i+4)];
if(j%2==1)
{
*ptrL++ =((val0 << 2)&0x3FC) | ((val4 >> 0) & 0x3);
*ptrL++ =((val1 << 2)&0x3FC) | ((val4 >> 2) & 0x3);
*ptrL++ =((val2 << 2)&0x3FC) | ((val4 >> 4) & 0x3);
*ptrL++ =((val3 << 2)&0x3FC) | ((val4 >> 6) & 0x3);
}
else
{
*ptrR++ =((val0 << 2)&0x3FC) | ((val4 >> 0) & 0x3);
*ptrR++ =((val1 << 2)&0x3FC) | ((val4 >> 2) & 0x3);
*ptrR++ =((val2 << 2)&0x3FC) | ((val4 >> 4) & 0x3);
*ptrR++ =((val3 << 2)&0x3FC) | ((val4 >> 6) & 0x3);
}
}
}
//ALOGD("m_PDBufSz %d, Stride %d\n", m_PDBufSz, i4Stride);
}
else
{
m_PDBuf = NULL;
}
return m_PDBuf;
}
NEON_OPT宏开启后有节省功耗的作用,为便于分析,可以只看NEON_OPT未定义情况。函数的主要功能有两个,一个是将原始的raw数据由原先的10bit存储转换为2byte存储,第二个是将L跟R的buf分开存储,需要注意的是DT=0x2b时L在前R在后,DT/=0x2b时R在前L在后。
ptrBufAddr即是tail数据的buffer地址,mode3下,sensor输出binning的预览数据,vc传输tail数据,所以tail数据的提取已经在sensor端做好了。
i4Stride表示tail一行数据的字节数,因为存在字节对齐,所以实际存储长度比xSzInByte大,buffer的偏移量计算应以实际为准。
3.曲线拉取线性度异常
拉取曲线步骤:
1.下载好菱形图并打印
2. 下载录入数据的表格
3. 读取otp中af的远近焦值,得出镜头手动停留的位置
4. 按指令跑一遍fullscan,然后求peak平均值
5. 再手动推动镜头到上述计算位置,抓取日志
6. 搜索关键字”handleAFS“
依次录入block0-8的数据,算法将图像分割为九块区域,分别计算相位差值,录入pd值需要*1000.