天天看點

Camera6 MTK camera啟動流程介紹

一、Camera架構介紹:

  Camera的架構分為Kernel 部分和hal部分,其中kernel部分主要有兩塊:

    (1)image sensordriver,負責具體型号的 sensor 的 id 檢測,上電,以及在preview、capture、

       初始化、3A等等功能設定時的寄存器配置;

    (2)isp driver,通過DMA将sensor資料流上傳;

  HAL層部分主要有三部分組成:

    (1)imageio,主要負責資料buffer上傳的pipe;

    (2)drv,包含imgsensor和isp的hal層控制;

    (3)feature io,包含各種3A等性能配置;

   這篇内容主要介紹開機過程中search sensor已經上電流程等内容。

二、Camera啟動流程:

1、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

  

Camera6 MTK camera啟動流程介紹

(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啟動流程:

  先來看整體的架構圖如下:

  

Camera6 MTK camera啟動流程介紹

1、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]);
    }
}
           

2、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;
}
           

3、上電相關:

(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是否正常通訊)。這部分調試過程中遇到的問題總結如下:

1、ID讀取不到,I2C不通:

  • 檢查上電時序,3項電壓(AVDD/DVDD/IOVDD)是否正确;
  • I2C位址及通道設定是否正确;
  • 檢查cfg_setting_imgsensor.cpp中MCLK和HW連結配置是否正确;

2、Camera啟動時間過長:

  • 檢查Sensor上電時序要求的延時,是否有偏長的情況;
  • 去掉多餘的I2C位址,因為大部分驅動會多添加一些位址;
  • OTP的加載調整到每次開機時第一次打開加載,之後不加載;
  • sensorInit如果時間過長,可以調節I2C speed(400->1000);

3、preview階段耗時:

  • 檢查streamOn/Off的耗時;
  • preview_init是否有較長時間的耗時
  • 以及延時操作使用mdelay代替msleep;
  • pre_delay_frame/cap_delay_frame丢幀操作是否合适;

4、低電流、功耗相關問題:

  • 檢查電壓是否都有下電成功,防止漏電;
  • 對于共pin的sensor,在操作時是否有做好workaround;
  • 将I2C寄存器單個讀寫,調整為連續讀寫的方式也有一定優化;
  • sensor的PIN是否有被其他子產品占用,異常操作的行為;

作者:frank_zyp

您的支援是對部落客最大的鼓勵,感謝您的認真閱讀。

本文無所謂版權,歡迎轉載。