V4L2 soc-camera 子系统
soc-camera的作者之所以引入这个子系统,我想一个主要目的就是把camera驱动划分为camera host端,camera device端,这样同一个camera sensor驱动就可以方便的移植到多个camera host下,而无须做较多的改动。
本译文取自kernel文档:Documentation/video4linux/soc-camera.txt
术语
在阅读本文档前先明确几个术语:
- camera / camera device / camera sensor - 是一个video-camera sensor chip,可以连接到各种系统和接口,典型的使用i2c来控制和配置设备,使用并行或者串行总线来传输数据。
- camera host - 一个接口,camera连接到它上面。存在于SoCs之上的特定的接口,比如PXA27X PXA3xx, SuperH, AVR32, i.MX27, i.MX31等等。我觉得i.MX51以及s5pv210物理上和这些host没有什么区别,只是二者的开发人员出于某种考虑而没有使用这个子系统实现。
- camera host bus - camera host和camera之间的连接,可以是串行的或者并行的,保存着数据线和控制线,比如clock,水平和垂直同步信号。
soc-camera subsystem的目的
soc-camera 子系统在camera host驱动和camera sensor驱动间提供了统一的接口。soc camera子系统向上提供了V4L2接口,当前仅支持open, mmap, ioctl, mmap,不支持read
这个子系统连接SoC video capture接口和CMOS camera sensor chips,使得sensor驱动可以在其他的hosts驱动上复用。子系统在设计时也考虑到了多个camera host接口的存在,以及每个接口存在多个camera sensor,尽管在大部分情况下仅有一个camera sensor。
现存的驱动
在2.6.27-rc4中,主线内核有两个host 驱动:PXA27x的pxa_camera.c和SuperH SoCs的sh_mobile_ceu_camera.c,有四个sensor驱动:mt9m001.c,mt9v002.c以及一个通用的 soc_camera_platform.c驱动。这个列表可能不是最新的,所以在你的源码树中查看,可能有更多的例子存在。
实际开发中,新的sensor驱动可以参照mt9m001.c和mt9v002.c这两个sensor驱动框架,根据项目所用的sensor稍加修改即可。
Camera host API
一个host camera driver使用soc_camera_host_register(struct soc_camera_host *)函数来注册。host object可以如下初始化
- static struct soc_camera_host pxa_soc_camera_host = {
- .drv_name = PXA_CAM_DRV_NAME,
- .ops = &pxa_soc_camera_host_ops,
- };
所有的方法都是通过struct soc_camera_host_ops
- static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = pxa_camera_add_device,
- .remove = pxa_camera_remove_device,
- .suspend = pxa_camera_suspend,
- .resume = pxa_camera_resume,
- .set_fmt_cap = pxa_camera_set_fmt_cap,
- .try_fmt_cap = pxa_camera_try_fmt_cap,
- .init_videobuf = pxa_camera_init_videobuf,
- .reqbufs = pxa_camera_reqbufs,
- .poll = pxa_camera_poll,
- .querycap = pxa_camera_querycap,
- .try_bus_param = pxa_camera_try_bus_param,
- .set_bus_param = pxa_camera_set_bus_param,
- };
.add是把camera sensor接入host;.remove方法从host卸载camera sensor时调用,除了执行host内部的初始化工作外,还应该调用camera sensor的.init和.release方法。(.add方法仅仅在soc_camera_open中被调用,也就是说当应用层第一个打开一个/dev/videoX设备节点时,此时进行camera host以及camera sensor的初始化是合理的)
.suspend和.resume方法实现host的电源管理功能, 他们要负责调用相应的sensor 方法。
.try_bus_param和.set_bus_param用来协商host和sensor间的物理连接参数。
.init_videobuf是当一个video-device被打开时调用,video-buffer管理的实现是完全依赖于特定的host
其余部分被V4L2的相应部分调用。
Camera API
Sensor drivers可以使用struct soc_camera_link,典型的由platform提供,用来代表sensor连接到的host总线,这个结构还提供了.power和.reset接口。本节所描述的代码和当前代码已经发生了很大的变化,所以暂时略过本节。
VIDIOC_S_CROP和VIDIOC_S_FMT调用的行为
VIDIOC_S_CROP:设置sensor窗口的位置和尺寸,单位是sensor的像素。改变sensor窗口尺寸时维持缩放因子没变,因此用户窗口尺寸也相应的变化了。
VIDIOC_S_FMT:设置用户窗口。通过修改缩放因子,尽可能的保持之前设置的窗口尺寸。如果sensor窗口无法保持不变,那么也可以修改。
soc-camera中有两个地方,可以进行缩放和剪切:camera sensor驱动,和camera host驱动。User ioctl首先发送到host dirver,然后再向下发送给camera driver。在camera sensor驱动执行缩放和剪切可能更有效,因为会节约camera总线带宽以及最大帧率(如果放大还成立吗,对于AD video芯片来说,缩放只能在host driver来做)。然而,如果camera sensor驱动无法实现要求的参数,那么host 驱动可以决定用它自己的缩放和剪切功能来满足用户需求。
Camera sensor驱动soc-camera core以及host驱动交互,完全是功能上的,不涉及任何数据传输。因而所有的camera sensor驱动应该用当前输出格式响应.g_fmt请求。这对于正确配置camera bus是必要的。s_fmt() 和try_fmt也不得不实现。sensor窗口和缩放因子也应该在camera sensor驱动内维护。按照V4L2 API,所有的capture驱动应该支持VIDIOC_CROPCAP ioctl,因此我们依赖于camera drivers实现.cropcap()。如果camera sensor驱动不支持cropping,那么可以选择不支持.s_crop,但是必须在camera host驱动实现cropping支持,起码g_crop方法必须支持。
用户设置的几何尺寸保存在struct soc_camera_device的.user_width和.user_height成员中,以供soc_camera core和host drivers使用。core在成功调用s_fmt后会修改这些成员,但是如果是在其他地方修改了他们,比如s_crop,那么host driver要负责更型他们。