MTK camera image sensor driver

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的規則是名字相同,所以我們要找到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,只是提供一個機制,至於策略是上層在控制。


發佈了18 篇原創文章 · 獲贊 11 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章