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,只是提供一個機制,至於策略是上層在控制。