3. Sensor驱动框架:从V4L2到高通Camera子系统

好,咱们进入正题。这一章要聊的是Sensor驱动的骨架——V4L2框架和高通Camera子系统的分层结构。说白了,就是搞清楚你的Sensor驱动到底活在哪个世界里,跟谁打交道。

我刚开始接触高通平台时,也被这一堆缩写搞晕过:KMD、UMD、HAL、V4L2……后来调了几个Sensor才明白,这些层级就像公司的组织架构——各司其职,谁也别越级汇报。

3.1 V4L2框架简介

V4L2,全称Video for Linux 2,是Linux内核里处理视频设备的通用框架。你想想看,摄像头、电视卡、图像采集卡……这些设备如果各写各的驱动,那应用层得疯掉。V4L2就是定了个规矩:所有视频设备都按同一套接口来。

在Camera领域,V4L2主要干这几件事:

  • 设备节点管理:每个Sensor对应一个/dev/videoX节点
  • 缓冲区管理:负责图像数据的DMA和内存映射
  • 控制接口:曝光、增益、帧率这些参数通过ioctl下发
  • 事件机制:比如帧同步信号、错误通知等

核心概念:V4L2把Camera抽象成一个"视频采集设备",应用层通过open/ioctl/mmap这套标准接口来操作。你的Sensor驱动,本质上就是实现V4L2规定的那些回调函数。

我记得第一次调Sensor时,死活不出图。后来发现是V4L2的buffer队列没配好——嗯,这个坑后面会细讲。

3.2 高通Camera子系统的分层结构

高通的Camera架构,从上到下分三层:KMD(内核态驱动)→ UMD(用户态驱动)→ HAL(硬件抽象层)。我画了张图,你看一眼就明白了。

高通Camera子系统分层结构 应用层(Camera App / Framework) HAL层(Camera HAL / Chi-CDK) UMD层(用户态驱动 / Camera Subsystem) KMD层(内核态驱动 / V4L2 + Sensor驱动) 硬件层(Sensor / ISP / MIPI PHY) 说明:KMD负责硬件操作,UMD做策略调度,HAL向上提供统一接口

每一层都有明确的职责:

层级 位置 主要职责 典型代码/模块
KMD 内核空间 Sensor寄存器读写、MIPI配置、V4L2设备注册 msm_sensor.c、cam_sensor_core.c
UMD 用户空间 Stream on/off调度、参数协商、电源管理 cam_sensor_dev.c、cam_sensor_io.c
HAL 用户空间 向上提供Camera API、Session管理、Pipeline配置 chi-cdk、camera_hal.cpp

我的经验:调试时80%的问题出在KMD和UMD的接口对接上。比如KMD上报的format跟UMD预期的不一致,直接导致黑屏。建议先确认KMD层的s_probe是否成功,再往上查。

3.3 Sensor驱动注册流程

一个Sensor从上电到出图,驱动注册是第一步。流程大致如下:

  1. 设备树解析:读取dtsi中的Sensor节点信息(I2C地址、GPIO、电源时序等)
  2. I2C/SPI总线匹配:根据compatible字符串找到对应的驱动
  3. probe函数执行:分配资源、初始化V4L2子设备、注册控制接口
  4. 电源上电:按dtsi配置的时序给Sensor供电
  5. 初始化序列下发:通过I2C写入Sensor的寄存器配置
  6. V4L2设备注册:创建/dev/videoX节点,等待应用层调用

这里有个关键点:probe成功不代表Sensor能出图。我遇到过probe返回0,但Sensor根本没起来的案例——后来发现是reset GPIO的极性搞反了。

3.4 platform_driver vs i2c_driver

写Sensor驱动时,第一个选择题就是:用platform_driver还是i2c_driver?

对比项 platform_driver i2c_driver
总线类型 虚拟平台总线 I2C物理总线
匹配方式 设备树compatible + 设备名 I2C设备地址 + compatible
适用场景 Sensor集成在SoC内部(如ISP内置) 外挂I2C接口的Sensor(绝大多数)
资源获取 通过platform_get_resource 通过i2c_client结构体
高通平台现状 较少使用,部分eSensor会用 主流方案,几乎所有外挂Sensor都用

避坑指南:我曾经在一个项目里误用了platform_driver来驱动I2C Sensor,结果probe函数根本不会被调用。因为platform_driver不会自动扫描I2C总线——你想想看,这就像拿着公交卡去坐地铁,闸机根本不认。

在高通平台上,99%的外挂Sensor都使用i2c_driver。注册方式如下:

static const struct i2c_device_id sensor_id[] = {
    { "imx586", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, sensor_id);

static const struct of_device_id sensor_of_match[] = {
    { .compatible = "sony,imx586" },
    { }
};
MODULE_DEVICE_TABLE(of, sensor_of_match);

static struct i2c_driver sensor_i2c_driver = {
    .driver = {
        .name = "imx586",
        .of_match_table = sensor_of_match,
    },
    .probe    = sensor_probe,
    .remove   = sensor_remove,
    .id_table = sensor_id,
};
module_i2c_driver(sensor_i2c_driver);

注意看,这里同时提供了i2c_device_id和of_match_table。前者用于老式板级配置,后者用于设备树。我个人习惯两个都写上,兼容性最好。

3.5 高通平台的特殊之处

高通在标准V4L2之上做了封装,叫Camera Subsystem(CAMSS)。它把Sensor驱动拆成了两部分:

  • 底层驱动:直接操作硬件,放在KMD层
  • 策略驱动:做流管理、电源管理,放在UMD层

这种拆分的好处是:换Sensor不用改上层代码。你只需要在KMD层实现新的cam_sensor_core接口,UMD层完全复用。

核心要点:在高通平台上,你的Sensor驱动最终会注册成一个cam_sensor_device,而不是直接暴露为V4L2设备。CAMSS框架会在UMD层帮你完成V4L2的封装。

嗯,这一章的内容就到这里。记住一句话:驱动框架是骨架,MIPI协议是血脉。骨架搭不对,后面调MIPI全是白费功夫。