Camera的框架分为Kernel 部分和hal部分,其中kernel部分主要有两块:
- image sensordriver,负责具体型号的 sensor 的 id 检测,上电,以及在preview、capture、初始化、3A等等功能设定时的寄存器配置;
-
isp driver,通过DMA将sensor数据流上传;
HAL层部分主要有三部分组成:
- imageio,主要负责数据buffer上传的pipe;
-
drv,包含imgsensor和isp的hal层控制;
-
feature io,包含各种3A等性能配置;
这篇内容主要介绍开机过程中search sensor已经上电流程等内容。
Camera启动流程
CameraService是在开机时启动的,启动后进行searchSensor的操作,会search系统多少camera,开机时的search操作,只进行camera支持数量的遍历,以及sensorID的读取操作,如下是hal部分的ASTAH绘制调用流程图,对应的接口的文件路径:
- HalSensorList:
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/HalSensorList.enumList.cpp
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/HalSensorList.cpp
- SeninfDrv:
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/mt6765/seninf_drv.cpp -
SensorDrv:
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/imgsensor_drv.cpp
(1)这里先看enumerateSensor_Locked完成的工作,直接看代码:
MUINT HalSensorList::searchSensors()
{
Mutex::Autolock _l(mEnumSensorMutex);
MY_LOGD("searchSensors");
return enumerateSensor_Locked();
}
MUINT HalSensorList::enumerateSensor_Locked()
{
SensorDrv *const pSensorDrv = SensorDrv::get();
SeninfDrv *const pSeninfDrv = SeninfDrv::createInstance();
//初始化seninf,配置ISP相关内容
pSeninfDrv->init();
//将所有的clk全部打开
pSeninfDrv->setAllMclkOnOff(ISP_DRIVING_8MA, TRUE);
pSensorDrv->init();
for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= max_index_of_camera; i++) {
if((ret = pSensorDrv->searchSensor((IMGSENSOR_SENSOR_IDX)i)) == SENSOR_NO_ERROR){
//query sensorinfo
querySensorDrvInfo((IMGSENSOR_SENSOR_IDX)i);
//fill in metadata
buildSensorMetadata((IMGSENSOR_SENSOR_IDX)i);
pSensorInfo = pSensorDrv->getSensorInfo((IMGSENSOR_SENSOR_IDX)i);
addAndInitSensorEnumInfo_Locked(
(IMGSENSOR_SENSOR_IDX)i,
mapToSensorType(pSensorInfo->GetType()),
pSensorInfo->getDrvMacroName());
}
}
}
(2)下面主要看下searchSensor的流程,这里有去获取sensorList的内容:
MINT32 ImgSensorDrv::searchSensor(IMGSENSOR_SENSOR_IDX sensorIdx)
{
GetSensorInitFuncList(&pSensorInitFunc);
featureControl(sensorIdx, SENSOR_FEATURE_SET_DRIVER, (MUINT8 *)&idx, &featureParaLen);
NSFeature::SensorInfoBase* pSensorInfo = pSensorInitFunc[idx].pSensorInfo;
}
GetSensorInitFuncList是获取到配置的sensorList的内容,此sensorList需要与kernel层配置的一致,不一致的话在打开camera时会出现异常:
//vendor/mediatek/proprietary/custom/mt6765/hal/imgsensor_src/sensorlist.cpp
MSDK_SENSOR_INIT_FUNCTION_STRUCT SensorList[] =
{
#if defined(IMX486_MIPI_RAW)
RAW_INFO(IMX486_SENSOR_ID, SENSOR_DRVNAME_IMX486_MIPI_RAW, CAM_CALGetCalData),
#endif
//.....
}
UINT32 GetSensorInitFuncList(MSDK_SENSOR_INIT_FUNCTION_STRUCT **ppSensorList)
{
if (NULL == ppSensorList) {
ALOGE("ERROR: NULL pSensorList\n");
return MHAL_UNKNOWN_ERROR;
}
*ppSensorList = &SensorList[0];
return MHAL_NO_ERROR;
}
对应的MSDK_SENSOR_INIT_FUNCTION_STRUCT的结构体如下:
typedef struct
{
MUINT32 sensorType;
MUINT32 SensorId;
MUINT8 drvname[32];
NSFeature::SensorInfoBase* pSensorInfo;
MUINT32 (*getCameraIndexMgr)(CAMERA_DATA_TYPE_ENUM CameraDataType, MVOID *pDataBuf, MUINT32 size);
MUINT32 (*getCameraCalData)(UINT32* pGetSensorCalData);
} MSDK_SENSOR_INIT_FUNCTION_STRUCT, *PMSDK_SENSOR_INIT_FUNCTION_STRUCT;
(3)featureControl的setDriver流程:
vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1/imgsensor_drv.cpp
MINT32 ImgSensorDrv::featureControl(
IMGSENSOR_SENSOR_IDX sensorIdx,
ACDK_SENSOR_FEATURE_ENUM FeatureId,
MUINT8 *pFeaturePara,
MUINT32 *pFeatureParaLen
)
{
//结构ACDK_SENSOR_FEATURECONTROL_STRUCT和kernel中一致
featureCtrl.InvokeCamera = sensorIdx;
featureCtrl.FeatureId = FeatureId;//SENSOR_FEATURE_SET_DRIVER
featureCtrl.pFeaturePara = pFeaturePara;
featureCtrl.pFeatureParaLen = pFeatureParaLen;
if (ioctl(m_fdSensor, KDIMGSENSORIOC_X_FEATURECONCTROL , &featureCtrl) < 0) {
LOG_ERR("[featureControl] Err-ctrlCode (%s)", strerror(errno));
return -errno;
}
return SENSOR_NO_ERROR;
}
kernel启动流程
先来看整体的框架图如下:
set clock设置时钟
static long imgsensor_ioctl(
struct file *a_pstFile,
unsigned int a_u4Command,
unsigned long a_u4Param)
{
case KDIMGSENSORIOC_X_SET_MCLK_PLL:
i4RetValue = imgsensor_clk_set(
&pgimgsensor->clk,
(struct ACDK_SENSOR_MCLK_STRUCT *)pBuff);
break;
//......
}
int imgsensor_clk_set(
struct IMGSENSOR_CLK *pclk, struct ACDK_SENSOR_MCLK_STRUCT *pmclk)
{
if (pmclk->on) {
clk_prepare_enable(pclk->imgsensor_ccf[mclk_index])
ret = clk_set_parent(
pclk->imgsensor_ccf[pmclk->TG],
pclk->imgsensor_ccf[mclk_index]);
} else {
clk_disable_unprepare(pclk->imgsensor_ccf[mclk_index]);
}
}
set driver
static long imgsensor_ioctl(
struct file *a_pstFile,
unsigned int a_u4Command,
unsigned long a_u4Param)
{
case KDIMGSENSORIOC_X_FEATURECONCTROL:
i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
break;
//......
}
static inline int adopt_CAMERA_HW_FeatureControl(void *pBuf)
{
/* copy from user */
switch (pFeatureCtrl->FeatureId) {
case SENSOR_FEATURE_SET_DRIVER:
{
MINT32 drv_idx;
psensor->inst.sensor_idx = pFeatureCtrl->InvokeCamera;
drv_idx = imgsensor_set_driver(psensor);
memcpy(pFeaturePara, &drv_idx, FeatureParaLen);
break;
}
}
}
遍历CONFIG_CUSTOM_KERNEL_IMGSENSOR的内容,然后看sensorList是否对应,并获取对应的下标,调用imgsensor_check_is_alive进行上下电并读取ID 的操作:
struct IMGSENSOR_INIT_FUNC_LIST kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR] = {
#if defined(XXXXXX_MIPI_RAW)
{XXXXXX_SENSOR_ID,
SENSOR_DRVNAME_XXXXXX_MIPI_RAW,
XXXXXX_MIPI_RAW_SensorInit},
#endif
//......
}
int imgsensor_set_driver(struct IMGSENSOR_SENSOR *psensor)
{
struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
struct IMGSENSOR_INIT_FUNC_LIST *pSensorList = kdSensorList;
//获取config的size
char *sensor_configs = STRINGIZE(CONFIG_CUSTOM_KERNEL_IMGSENSOR);
imgsensor_i2c_init(&psensor_inst->i2c_cfg,
imgsensor_custom_config[psensor->inst.sensor_idx].i2c_dev);
memcpy(psensor_list_config, sensor_configs+1, strlen(sensor_configs)-2);
//对应config字符串进行按空格进行拆解
driver_name = strsep(&psensor_list_config, " \0");
while (driver_name != NULL) {
for (j = 0; j < MAX_NUM_OF_SUPPORT_SENSOR; j++) {
//判断对应的init函数是否存在
if (pSensorList[j].init == NULL)
break;
else if (!strcmp(driver_name, pSensorList[j].name)) {
//如果在config中和sensorlist中同时有定义进行赋值
orderedSearchList[i++] = j;
break;
}
}
driver_name = strsep(&psensor_list_config, " \0");
}
for (i = 0; i < MAX_NUM_OF_SUPPORT_SENSOR; i++) {
//上面获取到的sensorlist的下标
drv_idx = orderedSearchList[i];
if (pSensorList[drv_idx].init) {
//调用对应驱动的init函数
pSensorList[drv_idx].init(&psensor->pfunc);
if (psensor->pfunc) {
psensor_inst->psensor_name =
(char *)pSensorList[drv_idx].name;
//到这里是重点,进行上电读取ID的操作
if (!imgsensor_check_is_alive(psensor)) {
ret = drv_idx;
}
}
}
}
}
下面看对应的上下电以及读取ID 的操作:
static inline int imgsensor_check_is_alive(struct IMGSENSOR_SENSOR *psensor)
{
struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
//上电
err = imgsensor_hw_power(&pgimgsensor->hw,
psensor,
psensor_inst->psensor_name,
IMGSENSOR_HW_POWER_STATUS_ON);
//读取ID
imgsensor_sensor_feature_control(
psensor,
SENSOR_FEATURE_CHECK_SENSOR_ID,
(MUINT8 *)&sensorID,
&retLen);
if (sensorID == 0 || sensorID == 0xFFFFFFFF) {
pr_info("Fail to get sensor ID %x\n", sensorID);
err = ERROR_SENSOR_CONNECT_FAIL;
} else {
pr_info(" Sensor found ID = 0x%x\n", sensorID);
err = ERROR_NONE;
}
//下电
imgsensor_hw_power(&pgimgsensor->hw,
psensor,
psensor_inst->psensor_name,
IMGSENSOR_HW_POWER_STATUS_OFF);
return err ? -EIO:err;
}
上电相关
(1)上电时序配置
struct IMGSENSOR_HW_POWER_INFO {
enum IMGSENSOR_HW_PIN pin;
enum IMGSENSOR_HW_PIN_STATE pin_state_on;
u32 pin_on_delay;
enum IMGSENSOR_HW_PIN_STATE pin_state_off;
u32 pin_off_delay;
};
struct IMGSENSOR_HW_POWER_SEQ sensor_power_sequence[] = {
//……
#if defined(XXXXXX_MIPI_RAW)
{
SENSOR_DRVNAME_XXXXXX_MIPI_RAW,
{
{RST, Vol_Low, 0},
{DVDD, Vol_1100, 1},
{AVDD, Vol_2800, 1},
{DOVDD, Vol_1800, 1},
{RST, Vol_High, 1},
{SensorMCLK, Vol_High, 0},
},
},
#endif
}
对应的控制的流程如下:
static enum IMGSENSOR_RETURN imgsensor_hw_power_sequence(
struct IMGSENSOR_HW *phw,
enum IMGSENSOR_SENSOR_IDX sensor_idx,
enum IMGSENSOR_HW_POWER_STATUS pwr_status,
struct IMGSENSOR_HW_POWER_SEQ *ppower_sequence,
char *pcurr_idx)
{
ppwr_info = ppwr_seq->pwr_info;
// 上电
while (ppwr_info->pin != IMGSENSOR_HW_PIN_NONE &&
ppwr_info < ppwr_seq->pwr_info + IMGSENSOR_HW_POWER_INFO_MAX) {
if (pwr_status == IMGSENSOR_HW_POWER_STATUS_ON &&
ppwr_info->pin != IMGSENSOR_HW_PIN_UNDEF) {
pdev = phw->pdev[psensor_pwr->id[ppwr_info->pin]];
if (pdev->set != NULL)
//调用GPIO或者regulator的set 电压操作,这里的pdev在imgsensor_probe中已经设置
pdev->set(
pdev->pinstance,
sensor_idx,
ppwr_info->pin,
ppwr_info->pin_state_on);
mdelay(ppwr_info->pin_on_delay);
}
// 从上到下依次上电
ppwr_info++;
pin_cnt++;
}
// 下电操作
if (pwr_status == IMGSENSOR_HW_POWER_STATUS_OFF) {
while (pin_cnt) {
//从下到上依次下电
ppwr_info--;
pin_cnt--;
if (ppwr_info->pin != IMGSENSOR_HW_PIN_UNDEF) {
pdev =
phw->pdev[psensor_pwr->id[ppwr_info->pin]];
mdelay(ppwr_info->pin_on_delay);
if (pdev->set != NULL)
pdev->set(
pdev->pinstance,
sensor_idx,
ppwr_info->pin,
ppwr_info->pin_state_off);
}
}
}
/* wait for power stable */
if (pwr_status == IMGSENSOR_HW_POWER_STATUS_ON)
mdelay(5);
return IMGSENSOR_RETURN_SUCCESS;
}
总结
通过上面的代码流程,可以知道上开机时,camera模块先会将所有的MCLK打开,然后对依次对对应的sensor进行上电,读取ID(判断I2C是否正常通讯)。这部分调试过程中遇到的问题总结如下:
ID读取不到,I2C不通
- 检查上电时序,3项电压(AVDD/DVDD/IOVDD)是否正确;
- I2C地址及通道设置是否正确;
- 检查cfg_setting_imgsensor.cpp中MCLK和HW链接配置是否正确;
Camera启动时间过长
- 检查Sensor上电时序要求的延时,是否有偏长的情况;
- 去掉多余的I2C地址,因为大部分驱动会多添加一些地址;
- OTP的加载调整到每次开机时第一次打开加载,之后不加载;
- sensorInit如果时间过长,可以调节I2C speed(400->1000);
preview阶段耗时
-
检查streamOn/Off的耗时;
-
preview_init是否有较长时间的耗时
-
以及延时操作使用mdelay代替msleep;
-
pre_delay_frame/cap_delay_frame丢帧操作是否合适;
低电流、功耗相关问题:
- 检查电压是否都有下电成功,防止漏电;
- 对于共pin的sensor,在操作时是否有做好workaround;
- 将I2C寄存器单个读写,调整为连续读写的方式也有一定优化;
- sensor的PIN是否有被其他模块占用,异常操作的行为;