MTK pdaf bringup导通流程

科技   2024-09-19 08:09   上海  

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、开发环境

平台:mt6833

主摄sensor: s5kjn1sq

模组厂:qtech

马达:dw9800

二、前期准备工作

  1. 在MOL上download 官方bring up文档,01_02_Driver_Check-PDAF_Driver_Porting_Guide_release_V1.4.pdf(最新).
  2. 问模组厂提供.ini文档,该文档记录了pdaf的相关参数,我们参考该文档配置sensor驱动
  3. OTP map,需要确认otp烧录了af和pdaf的数据
  4. 问sensor原厂提供pdaf PDMAP guide文档,该文档记录了PD Tail数据的存储格式,后续做pd buf的L,R分离时需要参考该文档
  5. pd_s5kjn1sqmipiraw.cpp文件,如果有现成的就问供应商拿现成的,如果没有则需要自己coding

三、前期需确认事项

1. 确认af是正常工作的,找相关bringup人员确认或在main log中搜索关键字"MoveLensTo"

image.png

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"

image.png

确保otp数据跟模组厂提供的ini文档中的参数应该是一致的。

3. 找sensor vendor确认pdaf type

image.png

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("

image.png

发现BufType为0(NOTDEF),需要走查代码vendor/mediatek/proprietary/hardware/mtkcam/aaa/source/isp_6s/af_assist_mgr/af_assist_mgr.cpp的AFAssisMgr::config实现:
image.png

看实际走了哪条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图。

抓取转换后的图片如下:

 
image.png
image.png

转换后的图像上下应该是亮度一致,并且图案相同的。拍摄的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在后。

image.png

ptrBufAddr即是tail数据的buffer地址,mode3下,sensor输出binning的预览数据,vc传输tail数据,所以tail数据的提取已经在sensor端做好了。

image.png

i4Stride表示tail一行数据的字节数,因为存在字节对齐,所以实际存储长度比xSzInByte大,buffer的偏移量计算应以实际为准。

image.png

3.曲线拉取线性度异常

拉取曲线步骤:

1.下载好菱形图并打印

2. 下载录入数据的表格

3. 读取otp中af的远近焦值,得出镜头手动停留的位置

4. 按指令跑一遍fullscan,然后求peak平均值

5. 再手动推动镜头到上述计算位置,抓取日志

6. 搜索关键字”handleAFS“

image.png

依次录入block0-8的数据,算法将图像分割为九块区域,分别计算相位差值,录入pd值需要*1000.

原文链接:

https://www.jianshu.com/p/979f67290bbb


参考文献:

【腾讯文档】Camera学习知识库
https://docs.qq.com/doc/DSWZ6dUlNemtUWndv

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

点个在看,为大佬点赞!

程序员Android
这是一个专注提供 Java Android 知识体系服务的公众号。 和你一起终身学习,小安愿做你成长道路上的垫脚石,不断垫高你的高度,衬托你的威仪。 风里雨里,我们一直在 Java Android 学习的路上支持你!
 最新文章