一个懒惰的文艺工程师!
分类: LINUX
2013-11-08 21:18:59
http://blog.csdn.net/sunweizhong1024/article/details/8497790
camera_sensor_driver
image_sensr首先要进行板极设备的初始化的工作:代码路径是在:/mediatek/platform/mt6577/kernel/core/mt6577_devs.c里面
#if1 ///defined(CONFIG_VIDEO_CAPTURE_DRIVERS)
retval =platform_device_register(&sensor_dev);
if (retval != 0){
return retval;
}
sensor_dev的实现如下:
staticstruct platform_device sensor_dev = {
.name = "image_sensor",
.id = -1,
};
上面是以platformbus 方式进行注册设备的,platformbus 的match的规则是名字相同,所以我们要找到imagesensor的driver就要找到以Platform方式进行注册,名字是”image_sensor”的驱动。
我们下grepcmd ,找到了driver的路径:/mediatek/custom/common/kernel/imgsensor/src/kd_sensorlist.c文件中:
staticstruct 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,
}
};
发现没有,上面的name是image_sensor,看下driver在哪里进行注册的?
/*=======================================================================
* CAMERA_HW_i2C_init()
*=======================================================================*/
staticint __init CAMERA_HW_i2C_init(void)
{
struct proc_dir_entry*prEntry;
i2c_register_board_info(CAMERA_I2C_BUSNUM,&kd_camera_dev, 1);//这里是注册一个i2c设备
if(platform_driver_register(&g_stCAMERA_HW_Driver)){//这里就是我们以platform方式注册的驱动函数
PK_ERR("failed toregister CAMERA_HW driver\n");
return -ENODEV;
}
//Register proc file forsensor register debug
prEntry =create_proc_entry("driver/camsensor", 0,NULL);//这里是注册Image_sensor的proc口,主要用于调试。
if (prEntry) {
prEntry->read_proc =CAMERA_HW_DumpReg_To_Proc;
prEntry->write_proc =CAMERA_HW_Reg_Debug;
}
else {
PK_ERR("add/proc/driver/camsensor entry fail \n");
}
atomic_set(&g_CamHWOpend,0);
atomic_set(&g_CamDrvOpenCnt,0);
atomic_set(&g_CamHWOpening,0);
return 0;
}
当我们platformbus 的deivice和drivermatch 成功后,就会调用drver的probe函数。
.probe =CAMERA_HW_probe,
staticint CAMERA_HW_probe(struct platform_device *pdev)
{
init_waitqueue_head(&kd_sensor_wait_queue);
returni2c_add_driver(&CAMERA_HW_i2c_driver);//这里是以i2c的方式进行注册cameradriver 整好和上上面的i2c方式注册设备一样。
}
其实image_sensor以platform方式注册,只是一个虚拟的方式进行注册的,主要好是以i2c方式进行注册的,下面就进入我们的主角,我们的i2c方式注册的driver
和platformbus一样,i2c同样有自己的一套匹配方式,他的匹配方式就是匹配id_table里面的名字和以i2c_register_board_info(CAMERA_I2C_BUSNUM,&kd_camera_dev, 1);方式进行注册到i2c上面的设备的名字进行匹配,匹配成功后,同样也会调用对应的probe函数:
*I2C Driver structure
********************************************************************************/
structi2c_driver CAMERA_HW_i2c_driver = {
.probe = CAMERA_HW_i2c_probe,
.remove =CAMERA_HW_i2c_remove,
.driver.name =CAMERA_HW_DRVNAME,
.id_table = CAMERA_HW_i2c_id,
};
看下这个Probe函数的实现:
staticint CAMERA_HW_i2c_probe(struct i2c_client *client, const structi2c_device_id *id)
{
int i4RetValue = 0;
PK_DBG("[CAMERA_HW]Attach I2C \n");
//get sensor i2c client
spin_lock(&kdsensor_drv_lock);
g_pstI2Cclient =client;//这里是获得我们的clientdevice,并且以platform方式进行注册
//set I2C clock rate
g_pstI2Cclient->timing =200;//200k
spin_unlock(&kdsensor_drv_lock);
//Register char driver
i4RetValue =RegisterCAMERA_HWCharDrv();
if(i4RetValue){
PK_ERR("[CAMERA_HW]register char device failed!\n");//调用这个函数进行注册char类型的设备,下面trace下这个函数是如何实现的
return i4RetValue;
}
//spin_lock_init(&g_CamHWLock);
PK_DBG("[CAMERA_HW]Attached!! \n");
return 0;
}
trace下registerCAMERA_HWCharDrv函数的实现:
*RegisterCAMERA_HWCharDrv
********************************************************************************/
inlinestatic int RegisterCAMERA_HWCharDrv(void)
{
struct device* sensor_device =NULL;
#ifCAMERA_HW_DYNAMIC_ALLOCATE_DEVNO
if(alloc_chrdev_region(&g_CAMERA_HWdevno, 0, 1,CAMERA_HW_DRVNAME))//分配一个字符设备
{
PK_DBG("[CAMERASENSOR] Allocate device no failed\n");
return -EAGAIN;
}
#else
if( register_chrdev_region( g_CAMERA_HWdevno , 1 , CAMERA_HW_DRVNAME) )//注册一个字符设备
{
PK_DBG("[CAMERASENSOR] Register device no failed\n");
return -EAGAIN;
}
#endif
//Allocate driver
g_pCAMERA_HW_CharDrv =cdev_alloc();
if(NULL ==g_pCAMERA_HW_CharDrv)
{
unregister_chrdev_region(g_CAMERA_HWdevno,1);
PK_DBG("[CAMERASENSOR] Allocate mem for kobject failed\n");
return -ENOMEM;
}
//Attatch file operation.
cdev_init(g_pCAMERA_HW_CharDrv,&g_stCAMERA_HW_fops);//关联到file_operation进入字符设备
g_pCAMERA_HW_CharDrv->owner= THIS_MODULE;
//Add to system
if(cdev_add(g_pCAMERA_HW_CharDrv,g_CAMERA_HWdevno, 1))//将我们分配的字符设备,attach上file_operation添加到system
{
PK_DBG("[mt6516_IDP]Attatch file operation failed\n");
unregister_chrdev_region(g_CAMERA_HWdevno,1);
return -EAGAIN;
}
sensor_class =class_create(THIS_MODULE, "sensordrv");//创建一个sensordrv类
if (IS_ERR(sensor_class)) {
int ret =PTR_ERR(sensor_class);
PK_DBG("Unable tocreate class, err = %d\n", ret);
return ret;
}
sensor_device =device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL,CAMERA_HW_DRVNAME);在sensordrv类里面创建一个CAMERA_HW_DRVNAME设备文件。
return 0;
}
上面只是一个虚拟的注册过程,我们还没有真正的到到我们的image_sensor设备。
看这个image_sensor的file_operation是如何实现的:
staticconst struct file_operations g_stCAMERA_HW_fops =
{
.owner = THIS_MODULE,
.open = CAMERA_HW_Open,
.release = CAMERA_HW_Release,
#ifdef USE_NEW_IOCTL
.unlocked_ioctl =CAMERA_HW_Ioctl
#else
.ioctl = CAMERA_HW_Ioctl
#endif
};
其实我最关注的就是我们手机上面有很多很多的driver,代码中是在哪里判断我们使用的是什么IC厂商的imagesensor。我找了很多代码,在mediatek的代码中我并没找到。
上层在操作设备的时候都是先open设备,获得文件指针以后就可以进行接下来的事情。
我们先分析open函数:
1、.open= CAMERA_HW_Open,
********************************************************************************/
staticint CAMERA_HW_Open(struct inode * a_pstInode,struct file * a_pstFile)
{
//
atomic_inc(&g_CamDrvOpenCnt);
return 0;
}//open函数里面没有进行任何的操作,就只是一个锁,防止多个
既然我们的open函数里面没有进行别的操作,而我们的file_operation里面就只有open和ioctl成员函数,所以说我们的任何操作imagesensor 的操作函数都是通过ioctl进行操作设备的。
2、.unlocked_ioctl= CAMERA_HW_Ioctl
看下unlocked_ioctl:
上面的两种ioctl的函数都是CAMERA_HW_Ioctl
看下CAMERA_HW_Ioctl函数的实现:
staticint CAMERA_HW_Ioctl(struct inode * a_pstInode,
structfile * a_pstFile,
unsignedint a_u4Command,
unsignedlong a_u4Param)
#endif
{
int i4RetValue = 0;
void * pBuff = NULL;
u32 *pIdx = NULL;
//PK_DBG("%x, %x\n",a_u4Command,a_u4Param);
mutex_lock(&kdCam_Mutex);
if(_IOC_NONE ==_IOC_DIR(a_u4Command))
{
}
else
{
pBuff =kmalloc(_IOC_SIZE(a_u4Command),GFP_KERNEL);//分配一个buffer,
if(NULL == pBuff)
{
PK_DBG("[CAMERASENSOR] ioctl allocate mem failed\n");
i4RetValue = -ENOMEM;
gotoCAMERA_HW_Ioctl_EXIT;
}
if(_IOC_WRITE &_IOC_DIR(a_u4Command))//判断是否可写?
{
if(copy_from_user(pBuff, (void *) a_u4Param,_IOC_SIZE(a_u4Command)))//将用户传递过来的命令参数复制到内核空间,接下来我们会根据这个数据进行选择
{
kfree(pBuff);
PK_DBG("[CAMERASENSOR] ioctl copy from user failed\n");
i4RetValue = -EFAULT;
gotoCAMERA_HW_Ioctl_EXIT;
}
}
}
pIdx = (u32*)pBuff;
switch(a_u4Command)
{
#if0
caseKDIMGSENSORIOC_X_POWER_ON:
i4RetValue =kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, true,CAMERA_HW_DRVNAME);//进行imagesensor 上电的工作,具体如何上电,我们会在下面进行讲解。
break;
caseKDIMGSENSORIOC_X_POWER_OFF:
i4RetValue =kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, false,CAMERA_HW_DRVNAME);//进行imagesensor power off 的动作
break;
#endif
caseKDIMGSENSORIOC_X_SET_DRIVER:
i4RetValue =kdSetDriver((unsigned int*)pBuff);//执行到这个cmd,将会调用kdSetDriver
//其实这个函数很重要,就是为我们对应的imagesensor 设置driver,就是设置提供给上层操作底层的interface
…....................
…......................
default :
PK_DBG("No such command\n");
i4RetValue = -EPERM;
break;
}
上层就是通过下ioctl的cmd进行操作底层的,我们就选几个cmd进行讲解下。
caseKDIMGSENSORIOC_T_OPEN:
i4RetValue= adopt_CAMERA_HW_Open();//当执行上面的cmd的时候,我们就会执行这个函数
inlinestatic intadopt_CAMERA_HW_Open(void)
{
UINT32err = 0;
#ifdefCONFIG_ARCH_MT6577
intret = 0;
//DDR2DRAM clock bug work around
if(2 == get_ddr_type()) {//DDR2
ret =set_dram_clk_gating(1);//设置dram寄存器
if(-1== ret) {
PK_ERR("ERROR: DRAM CLOCK GATING ERROR\n");
}
}
#endif
KD_IMGSENSOR_PROFILE_INIT();//返回当前的时间
//poweron sensor
if(atomic_read(&g_CamHWOpend) == 0) {
//turn on power
atomic_set(&g_CamHWOpening,1);
kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM)g_currDualSensorIdx, g_currSensorName,true,CAMERA_HW_DRVNAME);//为sensor上电,这个上电函数我们在下面进行讲解
//waitfor power stable
mDELAY(10);
KD_IMGSENSOR_PROFILE("kdModulePowerOn");
//
if(g_pSensorFunc) {//判断我们imagesensor 操作函数指针是否为NULL,如果为NULL,报错,因为我们就是靠这个操作函数集合去操作imagesensor 的,
//如果上层先下这个cmd的话,那么肯定是不行的,因为我们的g_pSensorFunc是NULL,并没有赋值,通过查询代码,发现是caseKDIMGSENSORIOC_X_SET_DRIVER:这个case;里面在做,所以上层肯定是先下这个cmd,然后才能执行我们现在讲解的cmd.这个cmd我们下面会进行讲解。
err =g_pSensorFunc->SensorOpen();//调用使用的imagesensor 的open函数,我们会对一个imagesensor 的接口函数进行举例讲解。
if(ERROR_NONE!= err) {
PK_DBG("ERROR:SensorOpen(), turn off power \n");
kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM)g_currDualSensorIdx, NULL,false,CAMERA_HW_DRVNAME);
}
//kaka_12_0112_2 add
else// Add for sensor provider HW infomodule
{
if(DUAL_CAMERA_MAIN_SENSOR== g_currDualSensorIdx){
memcpy(mainCameraName,g_currSensorName,CAM_NAME_LEN);
g_main_camera =mainCameraName;
}
elseif(DUAL_CAMERA_SUB_SENSOR== g_currDualSensorIdx) {
memcpy(subCameraName,g_currSensorName,CAM_NAME_LEN);
g_sub_camera =subCameraName;
}
}
//kaka_12_0112_2end
}
else{
PK_DBG("ERROR:NULL g_pSensorFunc\n");
}
KD_IMGSENSOR_PROFILE("SensorOpen");
}
if(err == 0 ) {
atomic_set(&g_CamHWOpend,1);
}
returnerr?-EIO:err;
} /*adopt_CAMERA_HW_Open() */
2.1、kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM)g_currDualSensorIdx, g_currSensorName,true,CAMERA_HW_DRVNAME);
--------->kdCISModulePowerOn(SensorIdx,currSensorName,On,mode_name)//这个就是camera的上电函数,函数定义在/mediatek/custom/common/kernel/camera/camera/kd_camera_hw.c里面。这个文件里面就是camera的上电函数,在mediatek平台下都共用这个函数。
3、Case: KDIMGSENSORIOC_X_SET_DRIVER
kdSetDriver函数的实现如下:
intkdSetDriver(unsigned int*pDrvIndex)
{
ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT*pSensorList = NULL;
unsignedintdrvIdx = (*pDrvIndex &KDIMGSENSOR_DUAL_MASK_LSB);//这里根据我们用户传递进来的参数转化为drvIdx,就是driverindex的选择
//setdriver for MAIN or SUB sensor
spin_lock(&kdsensor_drv_lock);
g_currDualSensorIdx =(CAMERA_DUAL_CAMERA_SENSOR_ENUM)((*pDrvIndex& KDIMGSENSOR_DUAL_MASK_MSB)>>KDIMGSENSOR_DUAL_SHIFT);
spin_unlock(&kdsensor_drv_lock);
if(0 != kdGetSensorInitFuncList(&pSensorList))//调用这个函数,取得所有添加的sensor的结构的首地址。下面看下他的实现:
{
PK_ERR("ERROR:kdGetSensorInitFuncList()\n");
return-EIO;
}
if(drvIdx < MAX_NUM_OF_SUPPORT_SENSOR)
{
if(NULL == pSensorList[drvIdx].SensorInit)
{
PK_ERR("ERROR:kdSetDriver()\n");
return-EIO;
}
pSensorList[drvIdx].SensorInit(&g_pSensorFunc);//调用我们所取得snsor的SensorInit函数,下面会进行看看这个Init函数的执行过程
if(NULL == g_pSensorFunc)
{
PK_ERR("ERROR:NULLg_pSensorFunc\n");
return-EIO;
}
//getsensor name
memcpy((char*)g_currSensorName,(char*)pSensorList[drvIdx].drvname,sizeof(pSensorList[drvIdx].drvname));
//returnsensor ID
*pDrvIndex = (unsignedint)pSensorList[drvIdx].SensorId;
PK_DBG("[kdSetDriver]:%d,%d,%s,%d\n",g_currDualSensorIdx,drvIdx,g_currSensorName,sizeof(pSensorList[drvIdx].drvname));
}
return0;
}
kdGetSensorInitFuncList:
UINT32kdGetSensorInitFuncList(ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT**ppSensorList)
{
if(NULL== ppSensorList)
{
printk("[kdGetSensorInitFuncList]ERROR:NULL ppSensorList\n");
return1;
}
*ppSensorList =&kdSensorList[0];//这里取出kdSensorList数组的第一个参数,这个KdSensorList很重要,当我们要添加一个新的sensor的时候,我们就会在这个数组里面进行填写。,这个添加的路径是在/mediatek/custom/common/kernel/imagesensor/src/kd_sensor_list.h这个头文件中
return0;
}// kdGetSensorInitFuncList()
上面我们是取得了整个数组的首地址:
下面会调用我们上层所应该调用的imagesensor
pSensorList[drvIdx].SensorInit:
调用SensorInit函数:
我们有很多的imagesensor ,我这里我只选取其中一个进行讲解:
看下这个init函数在干嘛?代码的路径是在/mediatek/custom/common/kernel/imagesensor/hi253_yuv_Sensor.c里面
4、UINT32HI253_YUV_SensorInit(PSENSOR_FUNCTION_STRUCT*pfFunc)
{
staticSENSOR_FUNCTION_STRUCTSensorFuncHI253=
{
HI253Open,
HI253GetInfo,
HI253GetResolution,
HI253FeatureControl,
HI253Control,
HI253Close
};
/*To Do : Check Sensor status here */
if(pfFunc!=NULL)
*pfFunc=&SensorFuncHI253;
returnERROR_NONE;
}/* SensorInit() */
上面我加红的函数就是driver里面提供的接口
4.1、HI253Open:
UINT32HI253Open(void)
{
kal_uint16SensorId = 0;
//1software reset sensor and wait (to sensor)
HI253SetPage(0x00);
HI253WriteCmosSensor(0x01,0xf1);
HI253WriteCmosSensor(0x01,0xf3);
HI253WriteCmosSensor(0x01,0xf1);
SensorId =HI253ReadCmosSensor(0x04);
Sleep(3);
SENSORDB("[HI253]HI253Open:Sensor ID %x\n",SensorId);
if(SensorId!= HI253_SENSOR_ID)
{
returnERROR_SENSOR_CONNECT_FAIL;
}
HI253InitSetting();
HI253InitPara();
returnERROR_NONE;
}通过函数实现就可以看出来,这里是在通过i2c控制imagesensor 的register,读取deivceid ,看是否链接上对应的imagesensor
4.2:HI253GetInfo
UINT32HI253GetInfo(MSDK_SCENARIO_ID_ENUMScenarioId,
MSDK_SENSOR_INFO_STRUCT*pSensorInfo,
MSDK_SENSOR_CONFIG_STRUCT*pSensorConfigData)
{
pSensorInfo->SensorPreviewResolutionX=HI253_PV_WIDTH;
pSensorInfo->SensorPreviewResolutionY=HI253_PV_HEIGHT;
pSensorInfo->SensorFullResolutionX=HI253_FULL_WIDTH;
pSensorInfo->SensorFullResolutionY=HI253_FULL_HEIGHT;
…................
…..................
switch(ScenarioId)
{
caseMSDK_SCENARIO_ID_CAMERA_PREVIEW:
caseMSDK_SCENARIO_ID_VIDEO_PREVIEW:
caseMSDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4:
caseMSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
caseMSDK_SCENARIO_ID_CAMERA_CAPTURE_MEM:
default:
pSensorInfo->SensorClockFreq=26;
pSensorInfo->SensorClockDividCount=3;
pSensorInfo->SensorClockRisingCount=0;
pSensorInfo->SensorClockFallingCount=2;
pSensorInfo->SensorPixelClockCount=3;
pSensorInfo->SensorDataLatchCount=2;
pSensorInfo->SensorGrabStartX= HI253_GRAB_START_X;
pSensorInfo->SensorGrabStartY= HI253_GRAB_START_Y;
break;
}
returnERROR_NONE;
}上面的函数一共传递进来了3个变量,第一个变量:是控制camera的工作模式,(拍照、摄像等等)
第2个参数:主要设置imagesensor 的频率的(时钟频率、预览频率、以及同步频率);第3个参数同样也是camera的设置,其实要看到底是在干嘛,只要看看这个参数是如何定义的就可以了。
4.3、HI253GetResolution
UINT32HI253GetResolution(MSDK_SENSOR_RESOLUTION_INFO_STRUCT*pSensorResolution)
{
pSensorResolution->SensorFullWidth= HI253_FULL_WIDTH;
pSensorResolution->SensorFullHeight= HI253_FULL_HEIGHT;
pSensorResolution->SensorPreviewWidth= HI253_PV_WIDTH;
pSensorResolution->SensorPreviewHeight= HI253_PV_HEIGHT;
returnERROR_NONE;
}/* HI253GetResolution() */
设置camera在预览模式下的高度、宽度等
4.4:HI253FeatureControl
UINT32HI253FeatureControl(MSDK_SENSOR_FEATURE_ENUM FeatureId,
UINT8*pFeaturePara,UINT32*pFeatureParaLen)
{
UINT16*pFeatureReturnPara16=(UINT16*) pFeaturePara;
UINT16*pFeatureData16=(UINT16*) pFeaturePara;
UINT32*pFeatureReturnPara32=(UINT32*) pFeaturePara;
UINT32*pFeatureData32=(UINT32*) pFeaturePara;
MSDK_SENSOR_CONFIG_STRUCT*pSensorConfigData=(MSDK_SENSOR_CONFIG_STRUCT *) pFeaturePara;
MSDK_SENSOR_REG_INFO_STRUCT*pSensorRegData=(MSDK_SENSOR_REG_INFO_STRUCT *) pFeaturePara;
switch(FeatureId)
{
caseSENSOR_FEATURE_GET_RESOLUTION:
*pFeatureReturnPara16++=HI253_FULL_WIDTH;
*pFeatureReturnPara16=HI253_FULL_HEIGHT;
*pFeatureParaLen=4;
break;
…...
…..
}
这个是上层会提供featureid,底层通过这个id进行不同case的执行为para和paralen赋值。
4.5:HI253Control
UINT32HI253Control(MSDK_SCENARIO_ID_ENUMScenarioId, MSDK_SENSOR_EXPOSURE_WINDOW_STRUCT *pImageWindow,
MSDK_SENSOR_CONFIG_STRUCT*pSensorConfigData)
{
switch(ScenarioId)
{
caseMSDK_SCENARIO_ID_CAMERA_PREVIEW:
caseMSDK_SCENARIO_ID_VIDEO_PREVIEW:
caseMSDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4:
HI253Preview(pImageWindow,pSensorConfigData);
break;
caseMSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
caseMSDK_SCENARIO_ID_CAMERA_CAPTURE_MEM:
HI253Capture(pImageWindow,pSensorConfigData);
break;
default:
break;
}
returnTRUE;
}/* HI253Control() */
这个函数和上面一样,也是提供控制的一个Interface
4.6:HI253Close
UINT32HI253Close(void)
{
returnERROR_NONE;
}/* HI253Close() */
这里的close没有执行任何工作,当然你也可以自己实现
通过mtk代码的分析,mtk的代码只是提供一个Interface,只是提供一个机制,至于策略是上层在控制。