【高通SDM660平台】Camera Kernel 驱动
在前面《【高通SDM660平台】Camera 驱动 Bringup Guide》中,我们学习了如何移植Camera 驱动,今天开始,我们要结合代码,学习下Kernel 中Camera 驱动具体的原理。
1. Camera Kernel 驱动
Kernel 驱动中 高通把Camera系统分为 Camera 和 Sensor 两部分:
Camera 部分是通用的代码逻辑,该部分由msm_cam 设备作为 video设备 与 userspace 进行交互,Qcom自已的MIPI,ISP,CPP 等硬件设备都属于Camera部分。
Sensor 可以理解为外部设备,是不同产商生产的Camera sensor模组。开发者分只需要配置不同的Sensor模组,将其注册到msm_cam设备上,创建好对应的video 设备,其他具体的接口逻辑均由Camera部分来实现。
在实际工作时,camera video设备主要是提供一个v4l2接口,Camera 驱动在接收到event消息后,会把该event消息及其参数以Post event形式发出到hal 层中,hal层接收到camera 驱动post 上来的event,来调用对应的逻辑,如果要操作sensor ,刚调用对应的 video设备就可以了。
接下来,我们依次来看看 msm_cam、sensor、v4l2 这几部分的代码逻辑:
2. qcom,msm-cam
msm-cam是在dts 中定义的,
在\kernel\msm-4.4\arch\arm\boot\dts\qcom\sdm660-camera.dtsi
中我们看到如下代码:
qcom,msm-cam@ca00000 {
compatible = "qcom,msm-cam";
reg = <0xca00000 0x4000>;
reg-names = "msm-cam";
status = "ok";
bus-vectors = "suspend", "svs", "nominal", "turbo";
qcom,bus-votes = <0 150000000 320000000 320000000>;
qcom,gpu-limit = <700000000>;
};
查找代码,我们可以看到,其注册的地方在\kernel\msm-4.4\drivers\media\platform\msm\camera_v2\msm.c
可以看出,msm-cam 是以平台驱动的形式注册在kernel 中。
static const struct of_device_id msm_dt_match[] = {
{.compatible = "qcom,msm-cam"},
{}
};
MODULE_DEVICE_TABLE(of, msm_dt_match);
static struct platform_driver msm_driver = {
.probe = msm_probe,
.driver = {
.name = "msm",
.owner = THIS_MODULE,
.of_match_table = msm_dt_match,
},
};
static int __init msm_init(void)
{
return platform_driver_register(&msm_driver);
}
初始化注册成功后,会调用msm_probe
函数
@\kernel\msm-4.4\drivers\media\platform\msm\camera_v2\msm.c
static struct v4l2_device *msm_v4l2_dev; // 初始化一个 v4l2_device 类型的结构体
static int msm_probe(struct platform_device *pdev)
{
struct msm_video_device *pvdev = NULL;
static struct dentry *cam_debugfs_root;
// 1. 初始化一个 v4l2_device 类型的结构体,并分配好结构体内存
msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev), GFP_KERNEL);
pvdev = kzalloc(sizeof(struct msm_video_device), GFP_KERNEL);
// 2. 分配 video_device 结构体内存
pvdev->vdev = video_device_alloc();
// ---> kzalloc(sizeof(struct video_device), GFP_KERNEL);
// 3. 分配 media_device 结构体内存
msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device), GFP_KERNEL);
strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME, sizeof(msm_v4l2_dev->mdev->model)); // msm_config
msm_v4l2_dev->mdev->dev = &(pdev->dev);
// 4. 注册 media_device , 使用的 v4l2
rc = media_device_register(msm_v4l2_dev->mdev);
pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; // V4L
pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID; // #define QCAMERA_VNODE_GROUP_ID 2
msm_v4l2_dev->notify = msm_sd_notify; // 用于发现对应的 subdev
pvdev->vdev->v4l2_dev = msm_v4l2_dev;
// 5. 注册 v4l2_device
rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
// 6. 注册 video_device设备
strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
pvdev->vdev->release = video_device_release;
pvdev->vdev->fops = &msm_fops; // 配置 video_device 的字符设备操作函数
pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops; // 配置 v4l2 IOCTRL
pvdev->vdev->minor = -1;
pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;
rc = video_register_device(pvdev->vdev, VFL_TYPE_GRABBER, -1);
// 7. 将当前 msm_video_device 结构
video_set_drvdata(pvdev->vdev, pvdev);
msm_session_q = kzalloc(sizeof(*msm_session_q), GFP_KERNEL);
if (WARN_ON(!msm_session_q))
goto v4l2_fail;
msm_init_queue(msm_session_q);
spin_lock_init(&msm_eventq_lock);
spin_lock_init(&msm_pid_lock);
mutex_init(&ordered_sd_mtx);
mutex_init(&v4l2_event_mtx);
INIT_LIST_HEAD(&ordered_sd_list);
cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR,
NULL);
if (!cam_debugfs_root) {
pr_warn("NON-FATAL: failed to create logsync base directory\n");
} else {
if (!debugfs_create_file(MSM_CAM_LOGSYNC_FILE_NAME,
0666,
cam_debugfs_root,
NULL,
&logsync_fops))
pr_warn("NON-FATAL: failed to create logsync debugfs file\n");
}
rc = cam_ahb_clk_init(pdev);
if (rc < 0) {
pr_err("%s: failed to register ahb clocks\n", __func__);
goto v4l2_fail;
}
of_property_read_u32(pdev->dev.of_node,
"qcom,gpu-limit", &gpu_limit);
goto probe_end;
v4l2_fail:
v4l2_device_unregister(pvdev->vdev->v4l2_dev);
register_fail:
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&pvdev->vdev->entity);
entity_fail:
media_device_unregister(msm_v4l2_dev->mdev);
media_fail:
kzfree(msm_v4l2_dev->mdev);
mdev_fail:
#endif
video_device_release(pvdev->vdev);
video_fail:
kzfree(pvdev);
pvdev_fail:
kzfree(msm_v4l2_dev);
probe_end:
return rc;
}