【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤

1、先梳理下camera的驱动架构,如下是驱动展开后的代码目录
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第1张图片
简单介绍下几个比较重要的文件:
inc->kd_imgsensor.h -----定义sensor id 和sensor name
src->mt6580->camera_hw->kd_camera_hw.c -----配置camera的上电时序
src->mt6580->kd_sensorlist.h------结合对应的sensor id和名字绑定对应的初始化函数
src->mt6580->kd_sensorlist.c -----camera驱动模块的加载,platform总线的注册
src->mt6580->xxx摄像头-----具体camera的驱动程序放置在这里

2、具体分析下kd_sensorlist.c文件和一个典型的camera驱动加载流程

//kd_sensorlist.c -----camera驱动模块的加载,platform总线的注册
//从后往前分析:
module_init(CAMERA_HW_i2C_init);
module_exit(CAMERA_HW_i2C_exit);

/*=======================================================================

  * CAMERA_HW_i2C_init()

  *=======================================================================*/

static int __init CAMERA_HW_i2C_init(void)

{
...
platform_device_register(&camerahw2_platform_device);
platform_driver_register(&g_stCAMERA_HW_Driver2)
proc_create("driver/camsensor", 0, NULL, &fcamera_proc_fops);//创建proc节点
...
}

static struct platform_driver g_stCAMERA_HW_Driver2 = {

	.probe = CAMERA_HW_probe2,//device与driver的名字匹配到之后就会跑prob函数

	.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

		   }
};

static int CAMERA_HW_probe2(struct platform_device *pdev)
{
	return i2c_add_driver(&CAMERA_HW_i2c_driver2);
}

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,
};

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

* CAMERA_HW_i2c_probe

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

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

{
...
	/* Register char driver */
	i4RetValue = RegisterCAMERA_HWCharDrv2();
...
}

/*******************************************************************************
* RegisterCAMERA_HWCharDrv
********************************************************************************/

static inline int RegisterCAMERA_HWCharDrv2(void)
{
...
alloc_chrdev_region(&g_CAMERA_HWdevno2, 0, 1, "kd_camera_hw_bus2")) 
major = MAJOR(g_CAMERA_HWdevno2);//得到主设备号
g_CAMERA_HWdevno2 = MKDEV(major, 0);//生成dev_t
/* Allocate driver */
	g_pCAMERA_HW_CharDrv2 = cdev_alloc();//用于动态申请一个cdev内存
 	/* Attatch file operation. */
	cdev_init(g_pCAMERA_HW_CharDrv2, &g_stCAMERA_HW_fops0);//初始化cdev的成员,并建立cdev和file_operation之间的联系
...
}
//对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进 行调用:

static const struct file_operations g_stCAMERA_HW_fops0 = {

	.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

};
其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口
/*******************************************************************************
* CAMERA_HW_Ioctl
********************************************************************************/
static long CAMERA_HW_Ioctl(struct file *a_pstFile,
			    unsigned int a_u4Command, unsigned long a_u4Param)

{
...
switch (a_u4Command) {
case KDIMGSENSORIOC_X_SET_DRIVER:
		i4RetValue = kdSetDriver((unsigned int *)pBuff);
		break;
	case KDIMGSENSORIOC_T_OPEN:
		i4RetValue = adopt_CAMERA_HW_Open();
		break;
...
}

通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:

Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中
/* set sensor driver */
#define KDIMGSENSORIOC_X_SET_DRIVER                 _IOWR(IMGSENSORMAGIC, 35, SENSOR_DRIVER_INDEX_STRUCT)
//  _IOWR()的用法参考其他博客(https://blog.csdn.net/u013256622/article/details/50922385)
...


分析各个函数的作用
/*******************************************************************************
* kdSetDriver
********************************************************************************/
int kdSetDriver(unsigned int *pDrvIndex)
{
ACDK_KD_SENSOR_INIT_FUNCTION_STRUCT *pSensorList = NULL;//定义init结构体
kdGetSensorInitFuncList(&pSensorList);// -->*ppSensorList = &kdSensorList[0];

pSensorList[drvIdx[i]].SensorInit(&g_pInvokeSensorFunc[i]);//执行init函数
/* get sensor name */
			memcpy((char *)g_invokeSensorNameStr[i],
			       (char *)pSensorList[drvIdx[i]].drvname,
			       sizeof(pSensorList[drvIdx[i]].drvname));
			/* return sensor ID */
			/* pDrvIndex[0] = (unsigned int)pSensorList[drvIdx].SensorId; */
}
到这里,整个 Camera  驱动从总线注册到完成具体 sensor  的初始化的流程就完成了,

CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。

3、举例一个具体驱动的实现,以ov8858为例子:

//以下这些函数就和之前的platform总线绑定起来了,整套流程就清晰起来了
static SENSOR_FUNCTION_STRUCT sensor_func = {
	open,		//打开之前会读sensor id如果正确识别就会继续跑下面的代码
	get_info,
	get_resolution,
	feature_control,
	control,
	close
};
static kal_uint32 open(void)
{
...
	/* 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];
		spin_unlock(&imgsensor_drv_lock);
		do {
			sensor_id = ((read_cmos_sensor(0x300B) << 8) | read_cmos_sensor(0x300C));
			if (sensor_id == imgsensor_info.sensor_id) {
				printk("i2c write id: 0x%x, sensor id: 0x%x\n",
					imgsensor.i2c_write_id, sensor_id);
				break;
			}
			printk("Read sensor id fail, i2c write id: 0x%x, sensor id: 0x%x\n\n",
				imgsensor.i2c_write_id, sensor_id);
			retry--;
		} while (retry > 0);
		i++;
		if (sensor_id == imgsensor_info.sensor_id)
			break;
		retry = 2;
	}
	...
	}
//UINT32 OV8858R2A_SensorInit(struct SENSOR_FUNCTION_STRUCT **pfFunc)
UINT32 OV8858_MIPI_RAW_SensorInit(PSENSOR_FUNCTION_STRUCT *pfFunc)
{
	/* To Do : Check Sensor status here */
	if (pfFunc != NULL)
		*pfFunc = &sensor_func;
	return ERROR_NONE;
}				/*      OV5693_MIPI_RAW_SensorInit      */

4、camera模块的移植,了解了代码流程后,移植也就相对简单了。

兼容camera:

(1) 项目上要兼容二、三供camera,拿到相应的资料及数据手册和驱动代码。
有两种情况:一是项目上已经做过相应的兼容;二是点亮全新的camera模组。
先去camera驱动代码目录下看有没有相关的代码,
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第2张图片

(2) 如果有相关的驱动代码说明平台已经兼容过该cam,你主要在分支中进行相应的配置即可,主要配置projectconfig.mk文件和hct_kernel.h文件即可。配置如下,要进行驱动和lens的匹配:
先根据邮件确认要兼容的camera型号;
image.png

(3) 找到相应驱动文件及lens文件的名字;
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第3张图片

(4) 由sensor id到时匹配到对应的lens

【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第4张图片

(5) lens的驱动代码位置,文件夹的名字到时会进行相应的配置
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第5张图片

(6) 在projectconfig.mk文件和hct_kernel.h文件添加相应配置;
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第6张图片

点亮全新的camera模组:
刚说完,活就来了,要在8.1平台上移植一个新的摄像头,s5k3h5,要点亮新摄像头,发现8.1平台上并没有移植这个摄像头,于是参考其他人的提交。
:需要的文件如下;
1、驱动代码;
2、效果参数;
3、lens的代码准备移植相关的lens
4、一些配置文件的移植。

项目经理并没有提供相关的datasheet,问了其他同事说直接从9.0上的代码移植过来就行。
也没有相关的效果参数代码,从相近的其他摄像头的效果参数代码中进行移植,把相关的名字都改成本摄像头的名字。
然后配置相关的宏控和名字保证编译通过。

如下修改点:当我们点亮一颗新摄像头是提交到主干的。

主干上提交s5k3h5camera新模组-kernel部分
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第7张图片

主干上提交s5k3h5camera新模组-hal部分
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第8张图片

提交二、三供后摄像头及二供前摄像头配置到相应的分支
【MTK驱动模块_2】camera的调试总结2--代码流程及移植步骤_第9张图片

由于9.0上的代码和8.1有点区别,刚开始编译会报很多的错误,不过不要着急,一步一步慢慢修改就行,大部分是一些变量定义了没有使用。修改了之后终于能保证编译通过了。
具体的修改点查看下我的提交即可,主要是上图列举的几大块部分。
注意:各个.h文件一定要添加完整,不然会导致驱动文件都没有调用到,工模里也不会有support休息。
注意当摄像头黑屏时,又没有检查出其他问题时,可以先排除下是不是摄像头的问题,换一个试下,或把这个摄像头接到其他用到该摄像头的项目看能不能亮,我检查了两天没有进展,最后换摄像头时,摄像头拔断了,才想到换摄像头试下,结果居然就亮了,(⊙o⊙)…,顿时心情难以言喻,不过这也让学会了一些调试的方法,加自己的log打印调试,看代码跑了哪些地方。

你可能感兴趣的:(MTK驱动总结,camera专题--MTK)