MTK平台camera驱动架构分析

MTK6580 AndroidO(android8.1)版本camera 驱动分析

CAMERA驱动整个框架分为:三个部分hal部分逻辑调用,kernel层的通用驱动sensorlist.c 和具体IC的驱动xxxx_mipi_raw.c

这里主要介绍kernel部分和HAL层部分。

camera开机流程:poweron上电开机,然后通过i2c地址匹配i2c通讯,rest和powerdown上电(上电代码在kd_camera_hw.c中的kdCISModulePowerOn,主要有VCAM:主要给ISP供电,VCAM_IO:数字IO电源,主要给I2C供电,VCAMA:模拟供电,主要给感光区和ADC部分供电,VCAMAF:主要给对焦马达供电;具体根据datasheet添加,有时会影响cts) ,读取sensor的ID(具体ic驱动里面的open和get_imgsensor_id都有读取id的操作,sensor id只要大于0、小于0xffffffff都是合法的。),然后软复位,下载preview参数为预览做准备,下载capture为拍照做准备,然后执行下电操作。

总结:

HAL层运行Search sensor这个线程

HAL层遍历sensorlist列表并挂载HAL层性能3A等一些参数获取的接口

HAL层下达setDriver的cmd,并下传正在遍历的sensorlist列表中的ID

Driver层根据这个ID,挂载Driver层sensorlist中对应的Sensorlist中对应的Sensor和具体Sensor底层操作接口(例如Sub_GC2355_MIPI_RAW_SensorInit)

HAL层对正确遍历的sensor下达check ID的指令

Driver层为对应sensor上电,通过I2C读取预存在寄存器中的sensor id

比较读取ID结果(配置的和读到的ID),不匹配,return error,继续遍历

匹配,HAL层下达其他指令收集sensor信息

最后sensor下电

一、MT6580 平台 Camera 驱动整体框架

整个框架分为三个部分hal部分逻辑调用,kernel层的通用驱动sensorlist.c 和具体IC的驱动 xxxx_mipi_raw.c,kernel起来后不会直接去访问硬件sensor,而是会注册相关的驱动,之后Android系统起来后会启动相关的服务如:camera_service,在camera服务中会直接去访问hal,kernel驱动,进而操作camera。

二、 Camera 驱动的具体实现

从vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/hal/sensor/imgsensor_drv.cpp中的impSearchSensor()函数说起。

-----------------------HAL 层部分----------------------------

文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/hal/sensor/imgsensor_drv.cpp
MINT32
ImgSensorDrv::impSearchSensor(pfExIdChk pExIdChkCbf)
{

GetSensorInitFuncList(&m_pstSensorInitFunc);

LOG_MSG(“SENSOR search start \n”);

sprintf(cBuf,"/dev/%s",CAMERA_HW_DEVNAME);
m_fdSensor = ::open(cBuf, O_RDWR);

for (i = 0; i < MAX_NUM_OF_SUPPORT_SENSOR; i++) {

err = ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );

err = ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);


}

GetSensorInitFuncList的实现:
文件:vendor/mediatek/proprietary/custom/mt6580/hal/imgsensor_src/sensorlist.cpp
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;
}
Sensor 列表的定义如下:
MSDK_SENSOR_INIT_FUNCTION_STRUCT SensorList[] =
{

#if defined(GC2365MIPI_RAW)
RAW_INFO(GC2365MIPI_SENSOR_ID, SENSOR_DRVNAME_GC2365MIPI_RAW, NULL),
#endif

#if defined(GC2355_MIPI_RAW_BAIKANG_M8112)
RAW_INFO(GC2355_SENSOR_ID, SENSOR_DRVNAME_GC2355_MIPI_RAW,NULL),
#endif

}

获取sensor列表后,紧接着通过:

err = ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );
err = ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);

访问kernel层的数据:
--------------------- Kernel 层驱动的实现 --------------------

  1. 针对前后摄注册platform 设备和驱动

文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/kd_sensorlist.c
static int __init CAMERA_HW_i2C_init(void)
{

if (platform_driver_register(&g_stCAMERA_HW_Driver)) //注册主摄platform 驱动
if (platform_driver_register(&g_stCAMERA_HW_Driver2)) //注册副摄platform 驱动


return 0;
}

主摄平台驱动的定义:

#ifdef CONFIG_OF
static const struct of_device_id CAMERA_HW_of_ids[] = {
{.compatible = “mediatek,camera_hw”,}, //主摄匹配规则
{}
};
#endif

static struct platform_driver g_stCAMERA_HW_Driver = {
.probe = CAMERA_HW_probe,
.remove = CAMERA_HW_remove,
.suspend = CAMERA_HW_suspend,
.resume = CAMERA_HW_resume,
.driver = {
.name = “image_sensor”,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW_of_ids,
#endif
}
};

副摄平台驱动的定义:

#ifdef CONFIG_OF
static const struct of_device_id CAMERA_HW2_of_ids[] = {
{.compatible = “mediatek,camera_hw2”,},//副摄匹配规则
{}
};
#endif

static struct platform_driver g_stCAMERA_HW_Driver2 = {
.probe = CAMERA_HW_probe2,
.remove = CAMERA_HW_remove2,
.suspend = CAMERA_HW_suspend2,
.resume = CAMERA_HW_resume2,
.driver = {
.name = “image_sensor_bus2”,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW2_of_ids,
#endif

}

};

主副摄cam在dts中(注册设备)定义设备信息:

kd_camera_hw1:kd_camera_hw1@15008000 {
compatible = “mediatek,camera_hw”; //这里必须和主摄一致
reg = <0x15008000 0x1000>; /* SENINF_ADDR */
vcama-supply = <&mt_pmic_vcama_ldo_reg>;
vcamd-supply = <&mt_pmic_vcamd_ldo_reg>;
vcamaf-supply = <&mt_pmic_vcamaf_ldo_reg>;
vcamio-supply = <&mt_pmic_vcamio_ldo_reg>;

};
kd_camera_hw2:kd_camera_hw2@15008000 {
compatible = “mediatek,camera_hw2”; //这里必须和副摄一致
reg = <0x15008000 0x1000>; /* SENINF_ADDR */
};

当内核启动后,会解析dts编译生成的dtb文件,注册里面定义的device,如果和驱动中定义compatible字段一致,则挂载启动。上面注册了两个platform 驱动g_stCAMERA_HW_Driver,g_stCAMERA_HW_Driver2,

如果compatible匹配成功会调用各自的probe函数CAMERA_HW_probe,CAMERA_HW_probe2

  1. 平台probe 函数的实现
    主摄probe,CAMERA_HW_probe的实现如下:
    static int CAMERA_HW_probe(struct platform_device *pdev)
    {
    #if !defined(CONFIG_MTK_LEGACY)
    mtkcam_gpio_init(pdev);
    mtkcam_pin_mux_init(pdev);
    #endif
    return i2c_add_driver(&CAMERA_HW_i2c_driver);
    }
    副摄probe,CAMERA_HW_probe的实现如下:
    static int CAMERA_HW_probe2(struct platform_device *pdev)
    {
    return i2c_add_driver(&CAMERA_HW_i2c_driver2);
    }
    从上可以看出在main/sub 的平台probe中分别注册了各自的i2c驱动CAMERA_HW_i2c_driver,
    CAMERA_HW_i2c_driver2,main sensor 的CAMERA_HW_i2c_driver定义如下:
    #ifdef CONFIG_OF
    static const struct of_device_id CAMERA_HW_i2c_of_ids[] = {
    { .compatible = “mediatek,camera_main”, },
    {}
    };
    #endif
    struct i2c_driver CAMERA_HW_i2c_driver = {
    .probe = CAMERA_HW_i2c_probe,
    .remove = CAMERA_HW_i2c_remove,
    .driver = {
    .name = CAMERA_HW_DRVNAME1,
    .owner = THIS_MODULE,
    #ifdef CONFIG_OF
    .of_match_table = CAMERA_HW_i2c_of_ids,
    #endif
    },
    .id_table = CAMERA_HW_i2c_id,
    };
    sub sensor 的CAMERA_HW_i2c_driver定义如下:
    #ifdef CONFIG_OF
    static const struct of_device_id CAMERA_HW2_i2c_driver_of_ids[] = {
    { .compatible = “mediatek,camera_sub”, },
    {}
    };
    #endif
    struct i2c_driver CAMERA_HW_i2c_driver2 = {
    .probe = CAMERA_HW_i2c_probe2,
    .remove = CAMERA_HW_i2c_remove2,
    .driver = {
    .name = CAMERA_HW_DRVNAME2,
    .owner = THIS_MODULE,
    #ifdef CONFIG_OF
    .of_match_table = CAMERA_HW2_i2c_driver_of_ids,
    #endif
    },
    .id_table = CAMERA_HW_i2c_id2,
    };

  2. I2c probe的实现
    从上可以看出main/sub sensor在各自的平台probe中,注册了i2c_driver,当各自的i2c_driver和设备匹配
    匹配规则:
    (1)当在dts有创建设备节点注册设备的时候:当i2c_driver和i2c_device的.compatible字段一致,就会执行i2c_driver里面的probe入口函数
    (2)当调用i2c_register_board_info注册i2c设备时,当i2c_driver驱动中ids_table中name字段匹配i2c设备中的name就可调用i2c_driver驱动中的probe函数。
    成功后,会调用各自的i2c_probe函数。

main sensor 的i2c_probe函数

CAMERA_HW_i2c_probe:
static int CAMERA_HW_i2c_probe(struct i2c_client *client, const struct i2c_device_id id)
{

/
Register char driver */
i4RetValue = RegisterCAMERA_HWCharDrv();


return 0;
}

sub sensor 的i2c_probe函数

CAMERA_HW_i2c_probe2:
static int CAMERA_HW_i2c_probe2(struct i2c_client *client, const struct i2c_device_id *id)
{

/* Register char driver */
i4RetValue = RegisterCAMERA_HWCharDrv2();


}

从上可以看出main/sub 在各自的i2c probe中,通过该调用 RegisterCAMERA_HWCharDrv ,
RegisterCAMERA_HWCharDrv2 注册了字符设备。各自注册cdev函数实现如下:

static inline int RegisterCAMERA_HWCharDrv(void)//main sensor 注册cdev
{


/* Attatch file operation. */
cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops); //初始化字符设备

/* Add to system */
cdev_add(g_pCAMERA_HW_CharDrv, g_CAMERA_HWdevno, 1) //注册到内核

//创建目录 /sys/class/sensordrv/
sensor_class = class_create(THIS_MODULE, “sensordrv”);
//创建目录/sys/class/sensordrv/kd_camera_hw
sensor_device = device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL, CAMERA_HW_DRVNAME1);


return 0;
}
static inline int RegisterCAMERA_HWCharDrv2(void)//sub sensor 注册cdev
{

/* Attatch file operation. /
cdev_init(g_pCAMERA_HW_CharDrv2, &g_stCAMERA_HW_fops0);//初始化字符设备
/
Add to system */
cdev_add(g_pCAMERA_HW_CharDrv2, g_CAMERA_HWdevno2, 1));//注册到内核
//创建目录 /sys/class/sensordrv2/
sensor2_class = class_create(THIS_MODULE, “sensordrv2”);
//创建目录/sys/class/sensordrv2/kd_camera_hw_bus2
sensor_device2 = device_create(sensor2_class, NULL, g_CAMERA_HWdevno2, NULL, CAMERA_HW_DRVNAME2);

return 0;
}

main/sub 创建各自的字符设备过程中绑定各自的 fops(fops是上层操作底层驱动的函数接口),g_stCAMERA_HW_fops和g_stCAMERA_HW_fops0

他们各自定义如下
static const struct file_operations g_stCAMERA_HW_fops = { //main sensor fops
.owner = THIS_MODULE,
.open = CAMERA_HW_Open,
.release = CAMERA_HW_Release,
.unlocked_ioctl = CAMERA_HW_Ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = CAMERA_HW_Ioctl_Compat,
#endif

};

static const struct file_operations g_stCAMERA_HW_fops0 = { //sub sensor fops
.owner = THIS_MODULE,
.open = CAMERA_HW_Open2,
.release = CAMERA_HW_Release2,
.unlocked_ioctl = CAMERA_HW_Ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = CAMERA_HW_Ioctl_Compat,
#endif

};

从上可以看出各自的fops指定了相同的Ioctl函数,意味着上层操作main/sub sensor 只需要对应一个底层的ioctl即可,至于sensor的区分可以借助idx,后面会讲到

/*******************************************************************************

  • CAMERA_HW_Ioctl
    ********************************************************************************/

static long CAMERA_HW_Ioctl(struct file *a_pstFile,unsigned int a_u4Command, unsigned long a_u4Param)
{


pIdx = (u32 *) pBuff;
switch (a_u4Command) {

case KDIMGSENSORIOC_X_SET_DRIVER:
i4RetValue = kdSetDriver((unsigned int *)pBuff);
break;

case KDIMGSENSORIOC_X_FEATURECONCTROL:
i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
break;

case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
i4RetValue = adopt_CAMERA_HW_CheckIsAlive();
break;


default:
PK_DBG(“No such command\n”);
i4RetValue = -EPERM;
break;

}


}

这里ioctl和上层一一对应,上层要控制camera 只需要传入相应的cmd和data即可。

------------------- HAL 调用Kernel 层驱动的逻辑 -------------------------

前面介绍了HAL层调用ioctl 和 kernel层注册驱动,接下来继续分析,HAL层调用后驱动具体的实现流程。

  1. ioctl 底层的实现

4.1 先来看ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );

当 KDIMGSENSORIOC_X_SET_DRIVER 被传下时,会调用 kernel 层的 kdSetDriver 接口

int kdSetDriver(unsigned int *pDrvIndex)
{

kdGetSensorInitFuncList(&pSensorList)) //获得sensor初始化列表

for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {

g_invokeSocketIdx[i] =(CAMERA_DUAL_CAMERA_SENSOR_ENUM) ((pDrvIndex[i] & KDIMGSENSOR_DUAL_MASK_MSB) >>KDIMGSENSOR_DUAL_SHIFT);
spin_unlock(&kdsensor_drv_lock);
drvIdx[i] = (pDrvIndex[i] & KDIMGSENSOR_DUAL_MASK_LSB); //上层传下来的id参数解析成两部分 g_invokeSocketIdx 区分目前正在匹配的是main还是sub, drvIdx 是sensor列表的序列号


pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]); //获取各个cam驱动中Init函数入口


}
return 0;
}

kdGetSensorInitFuncList 的实现:
UINT32 kdGetSensorInitFuncList(ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT **ppSensorList)
{
if (NULL == ppSensorList) {
PK_ERR("[kdGetSensorInitFuncList]ERROR: NULL ppSensorList\n");
return 1;
}
ppSensorList = &kdSensorList[0]; //获取sensorlist数组首地址
return 0;
}
/
kdGetSensorInitFuncList() */
kdSensorList定义如下:
文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/kd_sensorlist.h

ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR+1] =
{

#if defined(SUB_GC2355_MIPI_RAW)
{GC2355S_SENSOR_ID, SENSOR_DRVNAME_GC2355S_MIPI_RAW,Sub_GC2355_MIPI_RAW_SensorInit},
#endif


}
获取列表之后紧接着调用各自具体sensor的Init函数,这里以GC5025为例

UINT32 GC5025MIPI_RAW_SensorInit(PSENSOR_FUNCTION_STRUCT pfFunc)
{
/
To Do : Check Sensor status here */
if (pfFunc!=NULL)
pfFunc=&sensor_func;
return ERROR_NONE;
} /
GC5025MIPI_RAW_SensorInit */

从中可以看出,gc5025的Init函数地址传给了pfFunc,也就是后面在通用驱动中可以直接凭借pfun指针调用sensorlist中的驱动

4.2 再来看 ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);

当 KDIMGSENSORIOC_T_CHECK_IS_ALIVE 被传下时,会调用kernel层的 adopt_CAMERA_HW_Feature

Control接口

static inline int adopt_CAMERA_HW_CheckIsAlive(void)
{

/* power on sensor */
kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM *) g_invokeSocketIdx, g_invokeSensorNameStr,
true, CAMERA_HW_DRVNAME1);

if (g_pSensorFunc) {
for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {
if (DUAL_CAMERA_NONE_SENSOR != g_invokeSocketIdx[i]) {
err =
g_pSensorFunc->SensorFeatureControl(g_invokeSocketIdx[i],
SENSOR_FEATURE_CHECK_SENSOR_ID,
(MUINT8 ) &sensorID,
&retLen);
if (sensorID == 0) { /
not implement this feature ID /
PK_DBG
(" Not implement!!, use old open function to check\n");
err = ERROR_SENSOR_CONNECT_FAIL;
} else if (sensorID == 0xFFFFFFFF) { /
fail to open the sensor */
PK_DBG(" No Sensor Found");
err = ERROR_SENSOR_CONNECT_FAIL;
} else {

PK_INF(" Sensor found ID = 0x%x\n", sensorID);
snprintf(mtk_ccm_name, sizeof(mtk_ccm_name),
“%s CAM[%d]:%s;”, mtk_ccm_name,
g_invokeSocketIdx[i], g_invokeSensorNameStr[i]);
psensorResolution[0] = &sensorResolution[0];
psensorResolution[1] = &sensorResolution[1];
// don’t care of the result
g_pSensorFunc->SensorGetResolution(psensorResolution);
if(g_invokeSocketIdx[i] == DUAL_CAMERA_MAIN_SENSOR)
curr_sensor_id = 0;
else if(g_invokeSocketIdx[i] == DUAL_CAMERA_SUB_SENSOR)
curr_sensor_id = 1;
/* fill the cam infos with name/width/height */
snprintf(g_cam_infos, sizeof(g_cam_infos),"%s CAM[%d]:%s,Width:%d, Height:%d;",
g_cam_infos, g_invokeSocketIdx[i], g_invokeSensorNameStr[i],
sensorResolution[curr_sensor_id].SensorFullWidth, sensorResolution[curr_sensor_id].SensorFullHeight);

err = ERROR_NONE;
}
if (ERROR_NONE != err) {
PK_DBG
(“ERROR:adopt_CAMERA_HW_CheckIsAlive(), No imgsensor alive\n”);
}
}
}
} else {
PK_DBG(“ERROR:NULL g_pSensorFunc\n”);
}
} /* adopt_CAMERA_HW_Open() */

这个函数非常重要,它主要进行了以下几个动作,
1)通过 kdModulePowerOn 给Sensor上电
2)通过 SensorFeatureControl 读取SensorID

先看kdModulePowerOn的实现
int
kdModulePowerOn(CAMERA_DUAL_CAMERA_SENSOR_ENUM socketIdx[KDIMGSENSOR_MAX_INVOKE_DRIVERS],
char sensorNameStr[KDIMGSENSOR_MAX_INVOKE_DRIVERS][32], BOOL On, char *mode_name)
{
MINT32 ret = ERROR_NONE;
u32 i = 0;

for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {
if (g_bEnableDriver[i]) {
/* PK_XLOG_INFO("[%s][%d][%d][%s][%s]\r\n",FUNCTION,g_bEnableDriver[i],socketIdx[i],sensorNameStr[i],mode_name); */
#ifndef CONFIG_FPGA_EARLY_PORTING
ret = _kdCISModulePowerOn(socketIdx[i], sensorNameStr[i], On, mode_name);
#endif
if (ERROR_NONE != ret) {
PK_ERR("[%s]", func);
return ret;
}
}
}
return ERROR_NONE;
}

在kdModulePowerOn中又调用_kdCISModulePowerOn

int _kdCISModulePowerOn(CAMERA_DUAL_CAMERA_SENSOR_ENUM SensorIdx, char *currSensorName, BOOL On,
char *mode_name)
{

ret = kdCISModulePowerOn(SensorIdx, currSensorName, On, mode_name);

return ret;
}

在_kdCISModulePowerOn又调用 kdCISModulePowerOn 函数

文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/camera_hw/kd_camera_hw.c

//该函数为上下电函数,通过传入BOOL值来判断上/下电
int kdCISModulePowerOn(CAMERA_DUAL_CAMERA_SENSOR_ENUM SensorIdx, char *currSensorName, BOOL On,
char *mode_name)
{

u32 pinSetIdx = 0; /* default main sensor */

#define IDX_PS_CMRST 0
#define IDX_PS_CMPDN 4
#define IDX_PS_MODE 1
#define IDX_PS_ON 2
#define IDX_PS_OFF 3
#define VOL_2800 2800000
#define VOL_1800 1800000
#define VOL_1500 1500000
#define VOL_1200 1200000
#define VOL_1000 1000000

u32 pinSet[3][8] = {
/* for main sensor /
{ /
The reset pin of main sensor uses GPIO10 of mt6306, please call mt6306 API to set /
CAMERA_CMRST_PIN,
CAMERA_CMRST_PIN_M_GPIO, /
mode /
GPIO_OUT_ONE, /
ON state /
GPIO_OUT_ZERO, /
OFF state /
CAMERA_CMPDN_PIN,
CAMERA_CMPDN_PIN_M_GPIO,
GPIO_OUT_ONE,
GPIO_OUT_ZERO,
},
/
for sub sensor /
{
CAMERA_CMRST1_PIN,
CAMERA_CMRST1_PIN_M_GPIO,
GPIO_OUT_ONE,
GPIO_OUT_ZERO,
CAMERA_CMPDN1_PIN,
CAMERA_CMPDN1_PIN_M_GPIO,
GPIO_OUT_ONE,
GPIO_OUT_ZERO,
},
/
for main_2 sensor /
{
GPIO_CAMERA_INVALID,
GPIO_CAMERA_INVALID, /
mode /
GPIO_OUT_ONE, /
ON state /
GPIO_OUT_ZERO, /
OFF state */
GPIO_CAMERA_INVALID,
GPIO_CAMERA_INVALID,
GPIO_OUT_ONE,
GPIO_OUT_ZERO,
}
};

if (DUAL_CAMERA_MAIN_SENSOR == SensorIdx)
pinSetIdx = 0; //主(后)慑
else if (DUAL_CAMERA_SUB_SENSOR == SensorIdx)
pinSetIdx = 1;//副(前)慑
else if (DUAL_CAMERA_MAIN_2_SENSOR == SensorIdx)
pinSetIdx = 2;

/* power ON */
if (On) {

#if 0
ISP_MCLK1_EN(1);
ISP_MCLK2_EN(1);
ISP_MCLK3_EN(1);
#else
if (pinSetIdx == 0)
ISP_MCLK1_EN(1);
else if (pinSetIdx == 1)
ISP_MCLK2_EN(1);
#endif

printk("fangkuiccm %d ,currSensorName = %s pinSetIdx = %d ",LINE,currSensorName,pinSetIdx );

//通过DriverName来区分SensorIC
if (currSensorName && (0 == strcmp(SENSOR_DRVNAME_GC5025_MIPI_RAW, currSensorName))) {

/* First Power Pin low and Reset Pin Low */
if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN])
mtkcam_gpio_set(pinSetIdx, CAMPDN,
pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_OFF]);

if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMRST])
mtkcam_gpio_set(pinSetIdx, CAMRST,
pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_OFF]);

mdelay(50);

/* VCAM_A */
if (TRUE != _hwPowerOn(VCAMA, VOL_2800)) {
PK_DBG
("[CAMERA SENSOR] Fail to enable analog power (VCAM_A),power id = %d\n", //VCAMA就是AVDD模拟供电,主要给感光区和ADC部分供电
VCAMA);
goto kdCISModulePowerOn_exit;
}

mdelay(10);

/* VCAM_IO */
if (TRUE != _hwPowerOn(VCAMIO, VOL_1800)) {
PK_DBG
("[CAMERA SENSOR] Fail to enable IO power (VCAM_IO),power id = %d\n",//VDDIO数字IO电源主要给I2C部分供电;
VCAMIO);
goto kdCISModulePowerOn_exit;
}

mdelay(10);

if (TRUE != _hwPowerOn(VCAMD, VOL_1500)) {
PK_DBG
("[CAMERA SENSOR] Fail to enable digital power (VCAM_D),power id = %d\n", //数字供电,主要给ISP供电
VCAMD);
goto kdCISModulePowerOn_exit;
}

mdelay(10);

/* AF_VCC */
if (TRUE != _hwPowerOn(VCAMAF, VOL_2800)) {
PK_DBG
("[CAMERA SENSOR] Fail to enable analog power (VCAM_AF),power id = %d\n",//马达供电,主要给对焦马达供电
VCAMAF);
goto kdCISModulePowerOn_exit;
}

mdelay(50);

if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMRST]) {
mtkcam_gpio_set(pinSetIdx, CAMRST,
pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_OFF]);
mdelay(5);
mtkcam_gpio_set(pinSetIdx, CAMRST,
pinSet[pinSetIdx][IDX_PS_CMRST + IDX_PS_ON]);
}
mdelay(5);
/* enable active sensor */
if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN]) {
mtkcam_gpio_set(pinSetIdx, CAMPDN,
pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_ON]);
mdelay(5);
mtkcam_gpio_set(pinSetIdx, CAMPDN,
pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_OFF]);
}

mdelay(5);
}
}else{ //poweroff
if (currSensorName //上完电就要下电不然会造成漏电,最终会影响手机功耗
&& (0 == strcmp(SENSOR_DRVNAME_GC2355_MIPI_RAW, currSensorName))) {
#if 0
mt_set_gpio_mode(GPIO_SPI_MOSI_PIN, GPIO_MODE_00);
mt_set_gpio_dir(GPIO_SPI_MOSI_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_SPI_MOSI_PIN, GPIO_OUT_ONE);
#endif
/* First Power Pin low and Reset Pin Low */
if (GPIO_CAMERA_INVALID != pinSet[pinSetIdx][IDX_PS_CMPDN]) {
if (mt_set_gpio_mode
(pinSet[pinSetIdx][IDX_PS_CMPDN],
pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_MODE])) {
PK_DBG("[CAMERA LENS] set gpio mode failed!! (CMPDN)\n");
}
if (mt_set_gpio_dir(pinSet[pinSetIdx][IDX_PS_CMPDN], GPIO_DIR_OUT)) {
PK_DBG("[CAMERA LENS] set gpio dir failed!! (CMPDN)\n");
}
if (mt_set_gpio_out
(pinSet[pinSetIdx][IDX_PS_CMPDN],
pinSet[pinSetIdx][IDX_PS_CMPDN + IDX_PS_OFF])) {
PK_DBG("[CAMERA LENS] set gpio failed!! (CMPDN)\n");
}
}
}
}

上电操作完成后,紧接着读取SensorID,通用驱动使用 SensorFeatureControl 来读取ID如:

g_pSensorFunc->SensorFeatureControl(g_invokeSocketIdx[i],SENSOR_FEATURE_CHECK_SENSOR_ID,(MUINT8 *) &sensorID,&retLen);

这步操作会调用GC5025中的 feature_control 函数如下:
文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/gc2355_mipi_raw/gc5025mipi_Sensor.c
static kal_uint32 feature_control(MSDK_SENSOR_FEATURE_ENUM feature_id,
UINT8 *feature_para,UINT32 *feature_para_len)
{

LOG_INF(“feature_id = %d\n”, feature_id);
switch (feature_id) {

case SENSOR_FEATURE_CHECK_SENSOR_ID:
get_imgsensor_id(feature_return_para_32);
break;

default:
break;
}

return ERROR_NONE;
}

此时传入的cmd为SENSOR_FEATURE_CHECK_SENSOR_ID,就会调用 feature_control 中的get_imgsensor_id 再看 get_imgsensor_id 的实现

static kal_uint32 get_imgsensor_id(UINT32 *sensor_id)
{
kal_uint8 i = 0;
kal_uint8 retry = 2;
//sensor have two i2c address 0x6c 0x6d & 0x21 0x20, we should detect the module used i2c address
while (imgsensor_info.i2c_addr_table[i] != 0xff) {
spin_lock(&imgsensor_drv_lock);
imgsensor.i2c_write_id = imgsensor_info.i2c_addr_table[i]; //这是你根据sensor datasheet配置的sensor ID
spin_unlock(&imgsensor_drv_lock);
do {
*sensor_id = return_sensor_id(); //调用 return_sensor_id 读取IC的ID
if (*sensor_id == imgsensor_info.sensor_id) {
LOG_INF(“i2c write id: 0x%x, sensor id: 0x%x\n”, imgsensor.i2c_write_id,*sensor_id);
return ERROR_NONE;
}
LOG_INF(“Read sensor id fail, write id: 0x%x, id: 0x%x\n”, imgsensor.i2c_write_id,*sensor_id);
retry–;
} while(retry > 0);
i++;
retry = 2;
}

return ERROR_NONE;
}

再看return_sensor_id的实现

static kal_uint32 return_sensor_id(void)
{
return ((read_cmos_sensor(0xf0) << 8) | read_cmos_sensor(0xf1));
}
static kal_uint16 read_cmos_sensor(kal_uint32 addr)
{
kal_uint16 get_byte=0;

char pu_send_cmd[1] = {(char)(addr & 0xFF) };
iReadRegI2C(pu_send_cmd, 1, (u8*)&get_byte, 1, imgsensor.i2c_write_id);

return get_byte;

}

文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/kd_sensorlist.c

int iReadRegI2C(u8 *a_pSendData, u16 a_sizeSendData, u8 *a_pRecvData, u16 a_sizeRecvData,
u16 i2cId)
{
int i4RetValue = 0;

if (gI2CBusNum == SUPPORT_I2C_BUS_NUM1) {
spin_lock(&kdsensor_drv_lock);
g_pstI2Cclient->addr = (i2cId >> 1);
g_pstI2Cclient->ext_flag = (g_pstI2Cclient->ext_flag) & (~I2C_DMA_FLAG);

/* Remove i2c ack error log during search sensor /
/
PK_ERR(“g_pstI2Cclient->ext_flag: %d”, g_IsSearchSensor); */
if (g_IsSearchSensor == 1) {
g_pstI2Cclient->ext_flag = (g_pstI2Cclient->ext_flag) | I2C_A_FILTER_MSG;
} else {
g_pstI2Cclient->ext_flag = (g_pstI2Cclient->ext_flag) & (~I2C_A_FILTER_MSG);
}

spin_unlock(&kdsensor_drv_lock);
/* */
i4RetValue = i2c_master_send(g_pstI2Cclient, a_pSendData, a_sizeSendData);
if (i4RetValue != a_sizeSendData) {
PK_ERR("[CAMERA SENSOR] I2C send failed!!, Addr = 0x%x\n", a_pSendData[0]);
return -1;
}

i4RetValue = i2c_master_recv(g_pstI2Cclient, (char *)a_pRecvData, a_sizeRecvData);
if (i4RetValue != a_sizeRecvData) {
PK_ERR("[CAMERA SENSOR] I2C read failed!!\n");
return -1;
}
} else {
spin_lock(&kdsensor_drv_lock);
g_pstI2Cclient2->addr = (i2cId >> 1);

/* Remove i2c ack error log during search sensor /
/
PK_ERR(“g_pstI2Cclient2->ext_flag: %d”, g_IsSearchSensor); */
if (g_IsSearchSensor == 1) {
g_pstI2Cclient2->ext_flag = (g_pstI2Cclient2->ext_flag) | I2C_A_FILTER_MSG;
} else {
g_pstI2Cclient2->ext_flag = (g_pstI2Cclient2->ext_flag) & (~I2C_A_FILTER_MSG);
}
spin_unlock(&kdsensor_drv_lock);
i4RetValue = i2c_master_send(g_pstI2Cclient2, a_pSendData, a_sizeSendData);
if (i4RetValue != a_sizeSendData) {
PK_ERR("[CAMERA SENSOR] I2C send failed!!, Addr = 0x%x\n", a_pSendData[0]);
return -1;
}

i4RetValue = i2c_master_recv(g_pstI2Cclient2, (char *)a_pRecvData, a_sizeRecvData);
if (i4RetValue != a_sizeRecvData) {
PK_ERR("[CAMERA SENSOR] I2C read failed!!\n");
return -1;
}
}
return 0;
}

这一步完成I2c的读取,也就是说如果I2c配置正确,并且上电正确,到这一步就可以正确的读取ID,整个camera也就基本就调通了。

三、总结

通过上述分析,我们可以看出,camera驱动先是注册platform平台驱动,再注册I2c驱动,然后又为前后摄注册字符设备,封装底层方法CAMERA_HW_Ioctl,,上层访问底层驱动时候先是使用setdriver将具体IC的驱动入口获取,然后使用checkisalive对sensorlist中的IC进行上电,上电完成就通过i2c读取设备ID,到此为止,上层应用与底层驱动挂接完成,紧接着就是预览和拍照,不过这都是具体IC驱动的实现了。

简要概括过程:

1.开机时,camera完成了sensor框架的初始化,id检测,以及上下电操作;

Hal层在开机初始化调用文件vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/hal/sensor/imgsensor_drv.cpp中的

impSearchSensor(pfExIdChk pExIdChkCbf) 函数,这个函数执行4个功能:

1).用GetSensorInitFuncList(&m_pstSensorInitFunc)函数获取目前所有的camera sensor(需加log打印确认一下是获取全部sensor还是ProjectConfig.mk中配置的sensor)列表,这些 前后 camera 都在 projectconfig. mk已经设置。

2).用KDIMGSENSORIOC_X_SET_DRIVER向Kd_sensorlist.c(kernel-3.18\drivers\misc\mediatek\imgsensor\src\mt6580) 中的CAMERA_HW_Ioctl传值,通过case分支最终调用kdSetDriver函数,根据在projectconfig.mk文件里面的main sub camera的配置,找到相应的前后camera具体的驱动文件,即对于正在遍历的这颗sensor,挂接上具体的底层驱动接口Init函数。

3).用KDIMGSENSORIOC_T_CHECK_IS_ALIVE向Kd_sensorlist.c 中的CAMERA_HW_Ioctl(即前面讲到的上层操作底层的接口)传值,通过case分支最终调用adopt_CAMERA_HW_CheckIsAlive函数,在这个函数里开始给前面找到的所有camera上电,并通过向具体驱动里面的ioctrl函数传递SENSOR_FEATURE_CHECK_SENSOR_ID参数,最终通过case分支调用对应的函数通过I2C读取并核对是否为该sensor的 id,去识别具体的camera sensor id。

4).分别对前后已经配对了sensor id的camera执行m_pSubSensorInfo =m_pstSensorInitFunc.pSensorInfo;通过这个函数会调用具体驱动(这里以GC2145M 的前摄像头为例) UINT32GC2145mipiGetInfo()函数,这个函数是获取sensor IC preview capture vedio时的帧率 丢帧 打开时候的默认窗口,数据传输的type等基本信息。通过上面的4步,kernel内核已经识别了主板上的硬件camera IC了,并获取具体sensorIC的基本信息。

2.开应用时,camera会有上电,完成寄存器的初始配置,向上层传送基本参数及配置信息,以及preview和capture模式循环,客户端在点击对应camera图标开始进入previewcaputure vedio 应用操作时候,上层应用会启动上层的服务程序去调用Imgsensor_drv.cpp文件里面的ImgSensorDrv:open(MINT32 sensorIdx)函数,通过函数KDIMGSENSORIOC_T_OPEN()最终调用具体驱动UINT32 GC2145MIPIOpen(void)这个open函数。这个函数主要目的:通过调用GC2145MIPI_Sensor_Init函数,对相关capturepreview Vidio动作的3A寄存器进行初始化,从而启动相应的操作。
---------- 爱生活,爱安卓,爱Linux ----------

猜你喜欢

转载自blog.csdn.net/qq_30624591/article/details/85255985