Linux V4L2

V4L2 的本质,是"细粒度暴露"而非"高级抽象":它为每个硬件模块(subdev/video_device)提供初始化期与运行期的标准接口,暴露给 APP 间接调控。内核承担三件轻事——①注册机制(登记处)、②拓扑承载与校验(DT + Media Controller 台账 + link_validate)、③协商的统一流程(框架主导流程,规格细节由 APP 与驱动逐 pad 谈判)。绑定关系由驱动自己打结(media_create_pad_link),内核只承载、查询、校验,不代劳整合。运行时是"APP 经内核传话 + 驱动集体联动":物理连线固定,但逻辑选路(link/分叉/streams)在流间可变;没有框架级编排,s_stream 顺序由驱动自写——所以配置面十分像裸机。唯一被内核彻底收走、且必须收走的,是 vb2 的内存与流控状态机——因为 buffer 所有权是整条链上"最不能错、裸机最容易错"的部分。

V4L2 的扁平,根因是视频流特性逼着图像算法硬化,而硬化的实现形态各家私有——于是"概念有标准、实现无标准"。V4L2 的应对不是"被迫简陋",而是一次主动划界:把有标准的部分收进框架(vb2 状态机、media 拓扑、接口语义),把无标准的部分放行给驱动和厂商(编排顺序、寄存器、3A 调优)。所以它"灵活是主要目的"——灵活不是为了炫技,是为了不锁死八仙过海的各家 IP;"朴素暴力"不是附带的不成熟,而是"放权"必须付出的结构代价:框架一旦不立法,就注定看起来像一块地基而不是一座楼。证据就是你板子上的 vendor 栈——厂商只替换了"不能标准"的那部分,其余全套复用了 V4L2 的共性骨架。

V4L2 为链路上每个 IP Core 提供独立的 1:1 抽象(subdev/video_device),模块确实独立、扁平地躺在链路上。但模块间的通信与同步,内核只提供三样"基础设施",不代劳"动作":①通信词汇(v4l2_subdev_call 宏 = 喊话的标准姿势);②拓扑台账(media_pad_remote_pad_unique = 查"我上游是谁");③起流校验(media_pipeline_start = 链路合法性安检)。而"谁喊谁、什么顺序喊、失败怎么回滚、并发怎么同步",全部由驱动代码自己实现(s_stream 级联 + stream_lock + start_count)。所以准确说:内核提供"通信语言和地址簿",驱动执行"通信动作和编排"——这正是不收编编排权、保持单层扁平的本质。

一句话:V4L2 = 配置面裸机化(暴露 + 自编排)+ 数据面框架化(vb2 托管),中间由一张"驱动打结、内核查账"的逻辑图(Media Controller)连接。

V4L2 = 以 v4l2_subdev 为核心的硬件模块抽象(配置面,控制粒度近裸机)+ video_device 面向应用的流出口抽象 + vb2 提供的内存管理与流控状态机,三者通过 media graph(绳结)互连、由 v4l2_device(容器)统辖生命周期。

L5 系统架构层: 多摄/虚拟通道/低延迟/车规/ISP虚拟化
L4 图像质量层: 3A调优 + ISP PQ tuning(去噪/CCM/锐化)
L3 全栈排障层: 图像异常→定位到具体层(寄存器/时序/框架/APP)
L2 框架层:     V4L2/vb2/MC/libcamera 的架构与语义
L1 裸机层:     寄存器表/MIPI时序/时钟/DMA/电源

L5 系统架构(多摄/车规/ISP虚拟化)   ████████ 极稀缺   —— 架构师价
L4 3A/PQ tuning                  ████████ 稀缺+贵  —— 直接决定画质, 40-70K 在这
L3 全栈排障                      ██████   稀缺     —— 十年经验的护城河
L2 框架掌握(会用)                ████     中等     —— 能干活, 但可培训
L1 裸机寄存器(会抄)              ██       可替代   —— datasheet+参考代码

相机驱动做算法,而且这是重点。所以 L4 的真相是:画质 = 硬件 + 驱动 + 参数三方乘积,缺一个就崩。 而画质直接决定产品成败——安防夜视不行就退货、车载逆光不行就事故。这是"吃香"的第一根支柱:你做的事直接可见地决定产品生死。

V4L2 系统特征

1. V4L2 的硬件系统是复杂的、灵活的、无标准化的,其 IP 硬核通常被直接抽象为设备;

2. V4L2 子系统的一个鲜明的特点是 “扁平化” ,个性化的框架很简洁、朴素、不复杂,甚至基本上直接用字符设备和设备模型这样的基础设施就能满足框架设计目标;这是相比于 ASLSA 子系统它有个性化的 ASoC 子框架、还有 ASLSA-CORE 的 PCM/CONTROL/TIMER等设备的深度化抽象设计。所以,感官上讲 V4L2 以硬件 IP 模块为核心,框架只做简单的管理和协调。

3. 为了横向管理协调设备,V4L2 的核心框架远比"字符设备 + 设备模型"厚得多,它自己有大量实质性的框架层:

  • video_device / v4l2-dev:不是裸字符设备,而是在字符设备之上封装了完整的设备注册、minor 管理、ioctl 分发机制;

  • v4l2-subdev:专门抽象传感器、桥接器等内部 IP 单元,有自己独立的 ops 体系(core/video/pad/...);

  • Media Controller 框架:用 entity / pad / link 显式建模硬件流水线的拓扑结构;

  • videobuf2(vb2):整套内存管理框架(dma-buf / dma-contig / vmalloc),为驱动接管缓冲区生命周期;

  • v4l2-ctrls:控制框架,统一处理标准控制项的验证、存储、继承。

也就是说,"框架很简洁、朴素、只做简单管理和协调"这个感官判断低估了 v4l2-core 的实际厚度。更准确的说法是:V4L2 框架提供的是可选的、横向的工具箱(vb2、ctrl、subdev、mc),而不是纵向的、强制的流水线模型(ALSA 那样);V4L2的厚度是横向的‘零件仓库’的厚度(把框架扩展机制交给驱动) ,ALSA的厚度是纵向的‘生产流水线’的厚度(把驱动交给框架原生内置机制)

V4L2 面对的是形态高度多样、缺乏统一模型的视频硬件,因此框架不强制硬件符合某种标准流水线结构,而是提供一组横向的辅助框架(subdev、vb2、ctrl、media controller)供驱动按需组合;ALSA/ASoC 则相反,先建立与硬件无关的通用设备类(PCM/Control/Timer)和分层抽象(Card/Codec/Platform/DAI),再让硬件去适配这个模型。两者的框架本身都不"薄",差别在于抽象是纵向强制还是横向可选

详细解读

1. 你这个观察很深刻,点到了 V4L2 和其他子系统在架构上的本质区别。

子系统硬件形态需要的内核框架
DRM/GPU一个 GPU 管理内存+显示+渲染GEM/SHMEM 内存管理器、mode setting、fence 同步、上下文调度 — 复杂
ALSA声卡处理多条音频流PCM 核心、timer、MIDI、混音、延迟保证 — 复杂
Net网卡收发数据包协议栈(TCP/IP)、socket、skb 分配/克隆、路由表 — 极复杂
V4L2传感器→ISP→DMA 一串独立 IP不需要。每个 IP 已经是独立设备,V4L2 只需要串起来

V4L2 子系统以 IP 硬核抽象设计为核心。Sensor 是 I2C 设备,CSI-2 RX 是平台设备,ISP 也是平台设备。 它们都已经挂在各自的总线上,Linux 设备模型已经认识它们了。V4L2 不需要另搞一套"硬件管理框架":

Linux 设备模型已经做了:
  ┌─ I2C 核心 → imx219 sensor (i2c_client)
  ├─ 平台总线 → csi2-rx (platform_device)
  ├─ 平台总线 → isp-core (platform_device)
  └─ 平台总线 → vin-dma (platform_device)

V4L2 只需要做的:
  用 media_entity + media_link 把它们串起来
  用 video_device 开一个出口给用户态
  用 vb2 管一下 buffer 怎么流转
维度ALSA / ASoC 子系统V4L2 子系统
硬件抽象

深度垂直抽象

 

(PCM Stream / DAPM Widget / DPCM FE-BE)

横向颗粒化抽象

 

(v4l2_subdev / Pad / Link)

核心机制强依赖状态机、电源图谱自动寻路、音频时钟严苛同步依赖标准 cdev、videobuf2 缓冲区队列、ioctl 分发
框架角色“独裁者与导师”:定义了极其严格的接口规范和数据流转链路“管道工与协调员”:只建立 IP 间的链路,管理 Buffer,不干涉 IP 细节

你的"扁平"正是 V4L2 的核心设计哲学:硬件已经够独立了,框架不需要替硬件管理硬件。框架只需要定义大家怎么对话。这就是为什么 V4L2 的代码量远小于 DRM——不是功能少,而是复杂度分布不同。复杂度在硬件拓普里,不在框架里。

2. 这正是"基础设施"和"个性化机制"的区别。

Linux 内核作为操作系统,已经提供了一套基础设施层

Linux 内核基础设施
├── 设备模型 (driver model)     ← struct device, probe, driver_data
├── VFS 文件系统               ← cdev, file_operations, inode
├── 内存管理 (MM)              ← alloc_pages, dma_alloc_coherent
├── 中断管理                   ← request_irq, threaded IRQ
├── 时间/定时器                ← timer_list, hrtimer
└── 同步机制                   ← spinlock, mutex, wait_queue

V4L2 用到了这些基础设施中的哪些:

v4l2_device_register  → dev_set_drvdata(dev, v4l2_dev)  ← 设备模型
video_register_device → cdev_add + device_register       ← VFS + 设备模型
vb2_buffer           → dma_alloc_coherent                ← DMA API
v4l2_subdev          → i2c_new_client_device             ← I2C 核心(也是设备模型)

V4L2 一个"个性化机制"都没发明。 它只是把这些现成的基础设施按自己需要组合起来。

对比之下,DRM 发明了大量个性化机制

// DRM 自己做的东西(V4L2 没做)
drm_gem_object         // ← 自己管显存,不直接走 buddy allocator
drm_syncobj            // ← 自己管 GPU 同步 fence
drm_scheduler          // ← 自己管 GPU 任务调度
drm_atomic_state       // ← 自己管原子模式设置
drm_fb_helper          // ← 自己管 framebuffer 兼容
drm_mm                 // ← 自己管 GPU 地址空间

这些全是 DRM 特有的"个性化机制"

V4L2 之所以不需要这些,原因就是你前面说透了的:V4L2 管的硬件是扁平的、松耦合的独立 IP 阵列,不是 DRM 那样需要统一管理的复杂单芯片系统。 Sensor 自己就是 I2C 设备,不需要 V4L2 帮它"注册 I2C"。V4L2 做的事仅仅是:拉一个链表、开一个 /dev、管一批 buffer。

所以你的完整结论成立:

V4L2 不使用任何内核个性化机制来勾连。它所有的内核勾连全部走 Linux 设备模型和 VFS 这些基础设施。设备模型就是它的"内核存在感"的全部。

3. "扁平化"需要限定语境。 相比 ALSA 的分层强制抽象,V4L2 确实是"扁平"的——但这个扁平体现在框架不强制硬件结构,而不是"框架本身很薄"。两个概念混在一起就不严谨了。

4. "IP 硬核直接抽象为设备"大体对,但漏了一层。 现代 V4L2 实践中,一个视频硬件 IP 往往不是"一个设备",而是被拆成 subdev(硬件单元)+ video node(数据出口)+ media device(拓扑) 的组合描述。说"直接抽象为设备"容易让人误以为是一对一的朴素映射。

APP下潜链条

你看到 APP 只是对着 /dev/video0 简单调了一个 VIDIOC_S_FMT(设置 1080P),以为模块间没联动。实际上,VIDIOC_S_FMT 根本不是一个单纯的“设置”命令,而是一个在内核里“沿着流水线递归下发”的深度链式调用!

你在 APP 里做这一步时,内核幕后发生了一场“多米诺骨牌效应”:

【APP 层】
ioctl(fd, VIDIOC_S_FMT, &fmt)  ---> 作用于 /dev/video0
                                       │
┌──────────────────────────────────────┘
▼ 【内核层:V4L2 框架与驱动的隐式联动】
1. /dev/video0 的驱动 (rkisp_vdev) 收到 1080P 请求。
   └─> 它通过 Media Link 找到上游的 ISP Subdev。

2. 调用 ISP Subdev 的 pad_ops -> set_fmt():
   └─> ISP 说:“要输出 1080P YUV,我的 Sink Pad (输入端) 需要 RAW10 格式。”
   └─> ISP 自动向它的上游 MIPI-CSI Subdev 发起 set_fmt(RAW10) 请求。

3. 调用 MIPI-CSI Subdev 的 pad_ops -> set_fmt():
   └─> MIPI 说:“我要接收 RAW10,要求前面的 Sensor 也吐出 RAW10。”
   └─> MIPI 自动向它的上游 IMX415 Subdev 发起 set_fmt(RAW10) 请求。

4. 调用 IMX415 Sensor Subdev 的 pad_ops -> set_fmt():
   └─> IMX415 驱动查自己的 Register Table,找到最匹配 1080P RAW10 的 PLL 和 timing 参数。

看懂了吗?APP 不需要去管每个模块,是因为 /dev/video0 是这条链条的“总闸门”。 你拉动了总闸门,V4L2 框架就会沿着 DTS 建好的拓扑链路,递归调用每一个 Subdev 的 v4l2_subdev_pad_ops

底层硬件模块驱动的关联

你看到的“扁平”现象是这样的:

  • imx415.c 调用 v4l2_async_register_subdev() 注册 Sensor;

  • mipi-dphy.c 注册 DPHY;

  • rkisp.c 注册 ISP;

  • 最后一个 rkisp_vdev.c 注册了 /dev/video0 设备节点。

它们各自独立 probe,代码里甚至没有直接包含对方的头文件。它们到底是怎么知道彼此存在的?

答:靠设备树(DTS)里的 ports / port / endpoint 节点!连接关系的唯一真理来源(Single Source of Truth)不是 C 代码,而是 DTS!嗯你在 DTS 里看到的定义:

// IMX415 Sensor 节点
imx415: sensor@1a {
    port {
        imx415_out: endpoint {
            remote-endpoint = <&mipi_in>; // <--- 指向 MIPI DPHY
        };
    };
};

// MIPI DPHY 节点
mipi_dphy {
    port@0 {
        mipi_in: endpoint {
            remote-endpoint = <&imx415_out>; // <--- 互相指向
        };
    };
    port@1 {
        mipi_out: endpoint {
            remote-endpoint = <&isp_in>;   // <--- 指向 ISP
        };
    };
};

内核在初始化时做的事情:

  1. 异步匹配(v4l2-async):每个模块 probe 时,V4L2 核心层会去解析 DTS 里的 remote-endpoint

  2. 收集拼图:ISP 驱动会注册一个 notifier,说:“我需要等 DTS 里指定的 Sensor 和 MIPI 都注册完成。”

  3. 完成绑定(Bound):当最后一个 Sensor probe 成功后,V4L2 核心层触发 notifier_complete 回调。

  4. 自动建立拓扑网格:内核按照 DTS 的指示,自动把 Sensor -> MIPI -> ISP -> /dev/video0Media Controller Entity / Pad / Link 在内存里全打通了!

小结:驱动写得“扁平”,是因为连接关系交给了 DTS。代码只负责声明自己的输入输出端口(Pads)。

【DTS 阶段】
ALSA : DTS 里面写死 dai-link { cpu = <&i2s0>; codec = <&es8316>; };
V4L2 : DTS 里面写 ports / endpoint 互相指向 remote-endpoint。
      └─> 两者都在描述“电路板怎么连的”。

【初始化阶段】
ALSA : 声卡 Probe 时生成固定的 rtd (snd_soc_pcm_runtime)。
V4L2 : 解析 DTS,在内存生成独立的 Entity、Pad 和静态 media_link 图(尚无 rtd)。

【STREAMON 运行阶段】
ALSA : 直接使用已有的 rtd 去触发 soc_pcm_hw_params 和 trigger。
V4L2 : 1. 沿 media_link 反向搜索,动态生成 media_pipeline (动态 rtd);
       2. 逐级调用 link_validate() 校验格式/分辨率(只有全匹配,才是合法完整链路);
       3. 校验通过后,从下游到上游依次调用 s_stream(1) 启动数据流。

所以,DTS 提供了电路图,内核在 STREAMON 时沿着电路图去“跑线”,并通过 link_validate() 校验各关口的格式,最终确认并建立起一条合法且完整的运行链路!

V4L2 与 ALSA 的对比分析

一、 “算力已经由硬件模块实现了,不需要软件去做”

这就是 V4L2/MC 框架能够做到“轻软件、重硬件”的根本原因!

在音频(ALSA)里,CPU 有时还要充当“计算单元”,比如用软件去算重采样(SRC)、做通道混合(Mixer)、做格式转换(16bit 转 32bit)。

但在视频处理领域,4K@60fps 的数据量是恐怖的每秒 GB 级别!

  • 没有任何 CPU 能承受用软件代码去遍历每个像素点做 3A(自动曝光/白平衡/对焦)、去噪(De-noise)、畸变矫正(LDC)或者格式转换。

  • 所以,芯片厂商把所有的“图像处理算力”,全部做成了固化的硬件电路(HW IP Core)

Sensor 吐数据、MIPI D-PHY 抓数据、ISP 提亮/去噪/缩放、DMA 搬进内存——整个过程 100% 全是硬件电路在狂飙,软件(内核驱动)在数据传输过程中连一个像素都触碰不到!

驱动工程师写的 C 语言代码,本质上只是在写“配置控制”:

“把 0x01 号寄存器改成 0x20,让硬件缩放模块开启;把 0x05 号寄存器改成 0x10,让硬件 Bayer 转 RGB 模块启动。”

既然算力全在硬件,内核软件层当然不需要搞复杂的“数据加工和封装”,只需要当好“硬件寄存器配置员”就行了!

二、 “不需要改来改去去变化链路组合,仅仅是调整和协调格式”

这更是说中了摄像头/视频流水线与音频声卡最显著的区别!

在音频(ALSA/DPCM)里,应用层经常需要动态切链路

  • 比如:现在是电话响了,要动态把语音从“蓝牙 Headset 链路”切到“手机听筒链路”;

  • 比如:播放音乐时,要动态把音频流分流一份到“录音/回声消除(AEC)链路”。

但在摄像头(V4L2/MC)系统里,硬件电路在 PCB 板和 Silicon 芯片设计出来的那一刻,拓扑链路就已经高度固定了!

  • Sensor 的 MIPI 引脚物理上就是焊在主控芯片的 MIPI CSI 接收控制器上的;

  • MIPI 控制器的输出端在芯片内部就是用总线硬连到 ISP 的 Input 接口上的;

  • ISP 的 Output 接口就是直接绑在 DMA 控制器上的。

既然硬件链路组合是不怎么变的,那内核 MC(有向图)天天忙活的到底是什么?

就像你说的:就是纯粹在忙“格式的协调与对接合法性”!

 [ Sensor 硬件 ]          [ MIPI PHY 硬件 ]            [ ISP 算力硬件 ]
 吐出: RAW10             接收: 必须配置成 RAW10        输入: 必须设为 RAW10
 宽高: 3840x2160    ───► 传输: 4-Lane          ───►    加工: 裁剪/缩放为 1080P
 时钟: 800MHz            速率: 必须匹配 800MHz         输出: YUV422 (给 DMA)
      ▲                          ▲                          ▲
      │                          │                          │
  ┌───┴──────────────────────────┴──────────────────────────┴───┐
  │         内核 MC 框架唯一的任务:逐级对暗号(Link Validate)           │
  │     “大家格式、位深、时钟对齐了吗?对齐了就准许开机放行!”       │
  └─────────────────────────────────────────────────────────────┘

MC 框架在开机那一刻做的事情,就像流水线上的“关卡检查员”:

  1. 它问 Sensor:“你待会要吐什么?”(Sensor 说:“RAW10,4K 分辨率”);

  2. 它问 MIPI PHY:“你的通道能吃下 RAW10 4K 吗?”(PHY 说:“能,我已经配好 4-Lane 模式了”);

  3. 它问 ISP:“你的输入端准备好了吗?”(ISP 说:“准备好了,我的 Input Pad 也配成了 RAW10,且准备把画面缩放到 1080P 送给应用层”)。

只要这一圈暗号(Format/Bus Config)对齐了,链路就是“合法”的。 框架大手一挥,按下开机键,数据流就像闸门开闸放水一样,完全由硬件算力去奔跑了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值