【高通SDM660平台】Camera Kernel 驱动层代码逻辑分析

【高通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;
}

发布了349 篇原创文章 · 获赞 74 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Ciellee/article/details/105467156