文章目录
- Kernel 层驱动的实现
-
- Camera 开机流程
- Camera 驱动的文件结构
- Camera 驱动初始化流程
-
- Camera 入口函数 imgsensor_init
- 注册的平台驱动结构体 gimgsensor_platform_driver
- imgsensor_probe 探测函数的实现
- imgsensor_driver_register 注册函数的实现
- gimgsensor_file_operations 文件操作结构体的实现
- imgsensor_ioctl 的定义
- adopt_CAMERA_HW_FeatureControl 的定义
- imgsensor_set_driver 的实现
- kdSensorList 结构体中的实现
- GC2385_MIPI_RAW_SensorInit 的实现
- sensor_func 的实现
- open 的实现
- feature_control 的实现
- imgsensor_i2c_init 的实现
- imgsensor_hw_init 的实现
- imgsensor_i2c_create() 的实现
- gi2c_driver 结构体的实现
- 设备树中的配置
- 总结
平台 | os版本 |
---|---|
MT6739 | Android9.0 |
Kernel 层驱动的实现
camera 整个驱动框架分为三个部分: hal层 逻辑调用,kernel层 的通用驱动 sensorlist.c 和 具体IC 的驱动, 比如 gc2385_mipi_raw.c ,kernel 起来后不会直接去访问 硬件sensor ,而是会注册相关的驱动,之后 Android系统 起来后会启动相关的服务如: camera_service ,在 camera 服务中会直接去访问 hal层 , kernel驱动 ,进而操作 camera 。这里只分析 kernel层中 camera驱动的实现。
Camera 开机流程
Power On 上电开机,然后通过 i2c 地址匹配 i2c 通讯, reset 和 Power Down 上电 (上电代码在 kd_camera_hw.c 中的 kdCISModulePowerOn , VCAM 主要给 ISP 供电; VCAM_IO 是数字IO电源,主要给I2C 供电, VCAMA 是模拟供电,主要给 感光区 和 ADC 部分供电, VCAMAF 主要给对焦马达供电;具体使用可以根据 datasheet 添加,有时会影响 cts ),读取 sensor ID (具体 ic 驱动里面的 open 和 get_imgsensor_id 都有读取 id 的操作, sensor id 只要大于 0 、小于 0xffffffff 都是合法的),然后软复位,下载 preview 参数为预览做准备,下载 capture 为拍照做准备,然后执行下电操作。
Camera 驱动的文件结构
参考 MTK 的 mt6739_Sensor_Porting_guide 可以发现,在 kernel-3.18 中的 camera 驱动有 kd_sensorlist.c 和 kd_camera_hw.c 这两个文件,但是在 MT6739 平台的 kernel-4.4 中将这个文件拆分成了多个文件,如下图所示:
各个文件功能描述
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_hw.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_i2c.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_proc.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_sensor_list.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_legacy.c
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/mt6739/camera_hw/imgsensor_cfg_table.c
File | Decription(英文) | Decription(中文) |
---|---|---|
imgsensor.c | Sensor driver adapter and driver entry point | sensor 驱动适配器和驱动入口函数的实现 |
imgsensor_hw.c | Sensor power control | sensor 电源控制的实现 |
imgsensor_i2c.c | I2C read/write | I2C 读写函数的实现 |
imgsensor_proc.c | PROC related part | proc文件系统相关部分的实现 |
imgsensor_sensor_list.c | List of all sensors init function | 包含所有sensor初始化函数的表单 |
imgsensor_legacy.c | Legacy part of sensor. Mainly I2c related API | sensor的旧的接口部分。主要是与I2c相关API |
imgsensor_cfg_table.c | Sensor Power and I2C configruation table | sensor电源和I2C的配置表 |
Camera 驱动初始化流程
Camera 入口函数 imgsensor_init
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
module_init(imgsensor_init);
/* camera sensor入口函数 */
static int __init imgsensor_init(void)
{
PK_DBG("[camerahw_probe] start\n");
/* 注册一个平台驱动gimgsensor_platform_driver */
if (platform_driver_register(&gimgsensor_platform_driver)) {
PK_PR_ERR("failed to register CAMERA_HW driver\n");
return -ENODEV;
}
...
return 0;
}
注册的平台驱动结构体 gimgsensor_platform_driver
#ifdef CONFIG_OF /* 通过设备树进行match */
static const struct of_device_id gimgsensor_of_device_id[] = {
{
.compatible = "mediatek,camera_hw", },
{
}
};
#endif
static struct platform_driver gimgsensor_platform_driver = {
.probe = imgsensor_probe, /* 匹配成功会调用这个探测函数 */
.remove = imgsensor_remove,
.suspend = imgsensor_suspend,
.resume = imgsensor_resume,
.driver = {
.name = "image_sensor",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = gimgsensor_of_device_id, /* 保存与设备树compatible进行匹配的字符串 */
#endif
}
};
当 platform_devices 和 platform_driver 通过 match 函数匹配上后,会调用 imgsensor_probe 函数注册前后摄 camera 驱动。
imgsensor_probe 探测函数的实现
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
/* 平台driver成功与device匹配后调用imgsensor_probe */
static int imgsensor_probe(struct platform_device *pdev)
{
/* 分配设置注册camera驱动的字符设备文件/dev/kd_camera_hw,创建主次设备号 */
if (imgsensor_driver_register()) {
PK_PR_ERR("[CAMERA_HW] register char device failed!\n");
return -1;
}
gpimgsensor_hw_platform_device = pdev;
... /* 省略部分代码 */
/* camera硬件初始化,包含对power引脚的配置 */
imgsensor_hw_init(&pgimgsensor->hw);
/* 注册前后摄像头驱动 */
imgsensor_i2c_create();
imgsensor_proc_init();
atomic_set(&pgimgsensor->imgsensor_open_cnt, 0);
#ifdef CONFIG_MTK_SMI_EXT
mmdvfs_register_mmclk_switch_cb(mmsys_clk_change_cb, MMDVFS_CLIENT_ID_ISP);
#endif
return 0;
}
在 imgsensor_probe() 函数中一开始先为 camera 驱动分配设置注册了 camera 字符驱动设备,然后通过函数 imgsensor_i2c_create() 注册前后 camera 驱动。
imgsensor_driver_register 注册函数的实现
imgsensor_driver_register 由函数 imgsensor_probe() 调用,在 imgsensor_driver_register() 函数中实现了 camera 驱动的主次设备号创建,注册对应的设备文件操作结构体 gimgsensor_file_operations 。
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
static inline int imgsensor_driver_register(void)
{
int i, error = 0;
dev_t dev_no = MKDEV(IMGSENSOR_DEVICE_NNUMBER, 0);
/* 申请主设备号 */
/***************************************************************
* 让内核分配给我们一个尚未使用的主设备号,不是由我们自己指定的
*
* dev : alloc_chrdev_region函数向内核申请下来的主设备号
* baseminor: 次设备号的起始
* count : 申请次设备号的个数
* name : 执行 cat /proc/devices显示的名称
***************************************************************/
if (alloc_chrdev_region(&dev_no, 0, 1, IMGSENSOR_DEV_NAME)) {
PK_DBG("[CAMERA SENSOR] Allocate device no failed\n");
return -EAGAIN;
}
/* Allocate driver */
/* 给 gpimgsensor_cdev 分配内存,会在 cdev_del 中自动释放 */
gpimgsensor_cdev = cdev_alloc();
if (gpimgsensor_cdev == NULL) {
unregister_chrdev_region(dev_no, 1);
PK_DBG("[CAMERA SENSOR] Allocate mem for kobject failed\n");
return -ENOMEM;
}
/* Attatch file operation. */
/*************************************************************
* cdev_init 与 cdev_add 函数基本一致,但是多出来对 cdev->ops
* 的赋值,用来将 gimgsensor_file_operations 加入到系统中,
* gimgsensor_file_operations 中包含着实际处理与设备通信的
* 函数。
*************************************************************/
cdev_init(gpimgsensor_cdev, &gimgsensor_file_operations);
gpimgsensor_cdev->owner = THIS_MODULE;
/* Add to system */
/*************************************************************
* 初始化 cdev 后,需要通过 cdev_add 把它添加到系统中去。
* 传入 cdev 结构的指针 gpimgsensor_cdev , 起始设备编号,
* 以及设备编号范围。
*
* 可以在 /dev/ 目录下找到设备文件 "kd_camera_hw"
*************************************************************/
if (cdev_add(gpimgsensor_cdev, dev_no, 1)) {
PK_DBG("Attatch file operation failed\n");
unregister_chrdev_region(dev_no, 1);
return -EAGAIN;
}
/*************************************************************
* class_create 动态创建设备的逻辑类,并完成部分字段的初始化,
* 然后将其添加到内核中。创建的逻辑类位于/sys/class/。
*
* owner: 拥有者。一般赋值为THIS_MODULE。
* name: 创建的逻辑类的名称。
*************************************************************/
gpimgsensor_class = class_create(THIS_MODULE, "sensordrv");
if (IS_ERR(gpimgsensor_class)) {
int ret = PTR_ERR(gpimgsensor_class);
PK_DBG("Unable to create class, err = %d\n", ret);
return ret;
}
/************************************************************
* 通过 device_create 在 /sys/class/sensordrv/ 目录下创建一个
* 设备文件目录,目录名为 "kd_camera_hw"
************************************************************/
gimgsensor_device = device_create(gpimgsensor_class, NULL, dev_no, NULL, IMGSENSOR_DEV_NAME);
if (!gimgsensor_device) {
pr_err("Failed to create kd_camera_hw device\n");
return -1;
}
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
error = device_create_file(gimgsensor_device, &device_attrs[i]);
if (error) {
PK_DBG("device_attrs[%d] create failed!!!\n", i);
break;
}
}
return 0;
}
通过 cdev_init 函数将 gimgsensor_file_operations 设备文件操作结构体注册内核,这样就可以让上层应用调用时使用到底层相关的 open、read、write、ioctl 函数。
gimgsensor_file_operations 文件操作结构体的实现
imgsensor_ioctl 函数被赋值给 file_operations 结构体 gimgsensor_file_operations 的成员变量 .unlocked_ioctl 。
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
static const struct file_operations gimgsensor_file_operations = {
.owner = THIS_MODULE,
.open = imgsensor_open,
.release = imgsensor_release,
.unlocked_ioctl = imgsensor_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = imgsensor_compat_ioctl
#endif
};
在 gimgsensor_file_operations 中可以发现,camera 控制函数是有 imgsensor_ioctl 实现的。
imgsensor_ioctl 的定义
通过 imgsensor_ioctl 这个函数来提供 camera 硬件驱动的控制接口。
文件路径:
./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
static long imgsensor_ioctl(
struct file *a_pstFile,
unsigned int a_u4Command,
unsigned long a_u4Param)
{
int i4RetValue = 0;
void *pBuff = NULL;
...
switch (a_u4Command) {
case KDIMGSENSORIOC_X_GET_CONFIG_INFO:
i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
break;
case KDIMGSENSORIOC_X_GETINFO2:
i4RetValue = adopt_CAMERA_HW_GetInfo2(pBuff);
break;
case KDIMGSENSORIOC_X_FEATURECONCTROL:
i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
break;
case KDIMGSENSORIOC_X_CONTROL:
i4RetValue = adopt_CAMERA_HW_Control(pBuff);
break;
case KDIMGSENSORIOC_X_SET_MCLK_PLL:
i4RetValue = imgsensor_clk_set(&pgimgsensor->clk, (ACDK_SENSOR_MCLK_STRUCT