V4L2 subdev驱动开发:sensor与ISP协同 - 第2节
2.1 Subdev 在 V4L2 框架中的定位与作用
在嵌入式摄像头系统中,一个完整的视频采集链路通常由 Sensor(图像传感器)、ISP(图像信号处理器)、CSI 接收器 等多个硬件模块串联而成。V4L2(Video for Linux 2)框架引入 Subdev(Sub-device) 概念,正是为了将这条链路上的每个独立硬件单元抽象为可独立控制、可动态连接的软件实体。
2.1.1 Subdev 的主要作用
| 作用维度 | 具体说明 | 典型场景 |
|---|---|---|
| 硬件抽象 | 将 Sensor、ISP、MIPI 桥接器等硬件封装为统一的 subdev 对象 | Sensor 驱动注册为 subdev,提供曝光、增益控制接口 |
| 链路管理 | 通过 media controller 框架实现 subdev 之间的动态连接与路由 | ISP subdev 从 Sensor subdev 接收图像数据 |
| 控制分发 | 提供标准化的控制接口(格式、帧率、曝光、白平衡等) | 应用层通过 subdev 节点直接调节 Sensor 参数 |
| 事件通知 | 支持 subdev 间的事件传递(如帧同步、错误上报) | Sensor 完成曝光后通知 ISP 开始处理 |
2.1.2 Subdev 与 Video 节点的关系
V4L2 框架中存在两类核心设备节点:Video 节点(/dev/videoX) 和 Subdev 节点(/dev/v4l-subdevX)。它们的分工与协作关系如下:
- Video 节点:面向应用层,负责图像数据的采集、输出和流 I/O 控制(如
VIDIOC_QBUF、VIDIOC_DQBUF)。它是数据流的终点或起点。 - Subdev 节点:面向驱动层,负责硬件单元的配置与控制(如格式协商、曝光设置、增益调节)。它不直接处理图像数据流。
下图展示了典型的 Sensor + ISP 系统中 Subdev 与 Video 节点的协作关系:
2.2 Subdev 内部架构:ops、pad、state
每个 V4L2 subdev 驱动内部由三个核心组件构成:操作集(ops)、端口(pad) 和 状态(state)。理解这三者的关系是编写 subdev 驱动的基础。
2.2.1 操作集(ops)
struct v4l2_subdev_ops 是 subdev 驱动的核心接口集合,它定义了 subdev 对外提供的所有操作能力。ops 按功能分为多个子集:
| ops 子集 | 主要回调函数 | 功能描述 |
|---|---|---|
core |
init(), s_power(), ioctl() |
核心控制:初始化、电源管理、自定义 ioctl |
video |
s_stream(), g_frame_interval() |
视频流控制:启动/停止流、帧率查询 |
pad |
enum_mbus_code(), set_fmt(), get_fmt() |
Pad 操作:枚举格式、设置/获取格式 |
sensor |
g_skip_frames(), s_exposure() |
Sensor 专用:跳过帧数、曝光控制 |
ctrl |
s_ctrl(), g_ctrl() |
控制接口:V4L2 控制类的 get/set |
驱动注册时,通过 v4l2_subdev_init() 或 v4l2_i2c_subdev_init() 将 ops 绑定到 subdev 实例:
static const struct v4l2_subdev_ops sensor_subdev_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
.pad = &sensor_pad_ops,
};
static int sensor_probe(struct i2c_client *client) {
struct v4l2_subdev *sd;
// ... 分配 subdev 内存 ...
v4l2_i2c_subdev_init(sd, client, &sensor_subdev_ops);
// ... 其他初始化 ...
}
2.2.2 端口(pad)
Pad 是 subdev 的数据输入/输出端口,用于描述 subdev 与其他 subdev 或 video 节点之间的连接关系。每个 subdev 可以有多个 pad,分为 输入 pad 和 输出 pad。
- 输入 pad(SINK):接收来自上游 subdev 的图像数据。
- 输出 pad(SOURCE):向下游 subdev 或 video 节点发送图像数据。
Pad 的定义通过 struct v4l2_subdev_pad_ops 中的回调实现,核心是格式协商:
static struct v4l2_subdev_pad_ops sensor_pad_ops = {
.enum_mbus_code = sensor_enum_mbus_code, // 枚举支持的媒体总线格式
.get_fmt = sensor_get_fmt, // 获取当前格式
.set_fmt = sensor_set_fmt, // 设置格式(核心协商函数)
};
// pad 数组定义(Sensor 通常只有 1 个输出 pad)
static const struct media_entity_operations sensor_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static struct v4l2_subdev_pad_config sensor_pad_cfg[] = {
{
.pad = 0, // 输出 pad
.flags = MEDIA_PAD_FL_SOURCE,
},
};
set_fmt() 回调,确保整条链路的格式一致。
2.2.3 状态(state)
Subdev 的状态管理通过 struct v4l2_subdev_state 实现,它保存了 subdev 当前的工作参数,包括:
- Pad 格式:每个 pad 当前的媒体总线格式(
struct v4l2_mbus_framefmt)。 - 裁剪/缩放:每个 pad 的裁剪矩形(
struct v4l2_rect)和缩放比例。 - 帧间隔:当前帧率设置。
- 其他自定义状态:驱动开发者可扩展的私有数据。
状态管理在 v4l2_subdev_state 中通过 try 和 active 两套机制实现:
| 状态类型 | 用途 | 生命周期 |
|---|---|---|
| try 状态 | 格式协商时的试探性设置,用于验证格式是否支持 | 仅在 set_fmt() 调用期间有效 |
| active 状态 | 当前生效的工作参数,驱动实际硬件时使用 | 持续有效,直到被新的设置覆盖 |
下图展示了 subdev 内部 ops、pad、state 三者的关系:
2.2.4 三者的协同工作流程
以 Sensor subdev 设置分辨率为例,展示 ops、pad、state 的协同过程:
- 应用层调用:通过 Video 节点或 subdev 节点调用
VIDIOC_SUBDEV_S_FMT。 - 框架路由:V4L2 框架找到目标 subdev,调用其
pad_ops->set_fmt()回调。 - 格式协商:
set_fmt()内部检查请求的格式是否在enum_mbus_code()支持的列表中。 - 状态更新:如果格式有效,更新
state中的 pad 格式信息(try 或 active)。 - 硬件配置:通过
core_ops->s_power()或自定义函数,将格式写入 Sensor 硬件寄存器。 - 链路传播:如果格式变化影响下游,通过 media controller 通知相邻 subdev 重新协商。
2.3 本章小结
| 核心概念 | 一句话总结 |
|---|---|
| Subdev 作用 | 将 Sensor、ISP 等硬件单元抽象为可独立控制的驱动模块,实现管线化协作 |
| Subdev vs Video 节点 | Subdev 负责控制参数,Video 节点负责图像数据流,两者通过 media controller 协同 |
| ops | 定义 subdev 的操作能力(core/video/pad/ctrl 等子集) |
| pad | 定义 subdev 的数据输入/输出端口,支持格式协商与链路连接 |
| state | 保存 subdev 当前工作参数(格式、裁剪、帧率等),支持 try/active 双状态机制 |