mtk camera image 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的规则是名字相同,所以我们要找到imagesensordriver就要找到以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,

}

};

发现没有,上面的nameimage_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_sensorproc口,主要用于调试。

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 deivicedrivermatch 成功后,就会调用drverprobe函数。

.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_sensorplatform方式注册,只是一个虚拟的方式进行注册的,主要好是以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;

}

traceregisterCAMERA_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))//将我们分配的字符设备,attachfile_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_sensorfile_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里面就只有openioctl成员函数,所以说我们的任何操作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;

}

上层就是通过下ioctlcmd进行操作底层的,我们就选几个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_pSensorFuncNULL,并没有赋值,通过查询代码,发现是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.1kdModulePowerOn((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平台下都共用这个函数。






3Case: 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);//调用我们所取得snsorSensorInit函数,下面会进行看看这个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里面


4UINT32HI253_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.1HI253Open

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.3HI253GetResolution

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.4HI253FeatureControl

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的执行为paraparalen赋值。

4.5HI253Control

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.6HI253Close

UINT32HI253Close(void)

{

returnERROR_NONE;

}/* HI253Close() */

这里的close没有执行任何工作,当然你也可以自己实现


通过mtk代码的分析,mtk的代码只是提供一个Interface,只是提供一个机制,至于策略是上层在控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值