← 返回课程

Camera_Kernel驱动

Camera全栈开发(Qcom Camx) 第 20 / 30 节

第 18 章:Camera Kernel 驱动框架


本章导读

前面 17 章全在用户空间——App、Framework、HAL。这一章跨进内核空间

HAL 层通过 CSL 调用 ioctl 与内核驱动通信。当你调了 CSLPowerUp(),最终会通过系统调用进入内核,由内核驱动去操作硬件——拉高 GPIO、使能 regulator、通过 I2C 配置 sensor 寄存器。

这一章不讲"怎么写一个 kernel 驱动"——而是讲这个框架长什么样、由哪些模块组成、数据怎么在模块之间流转、出了问题怎么查


18.1 内核驱动框架全景——CAMSS

CAMSS 全景架构图

xxx使用的是 CAMSS(Camera SubSystem)——高通的相机子系统驱动框架。

用户空间                                            内核空间
─────────                                          ─────────

App → Framework → HAL → CSL
                          │ ioctl()
                          ▼
                    ┌──────────────────────────────────────────┐
                    │              CAMSS 驱动框架               │
                    │                                          │
                    │  ┌─────────────┐                          │
                    │  │ cam-req-mgr │  ← 请求调度器            │
                    │  └──────┬──────┘                          │
                    │         │                                 │
                    │    ┌────┴────────────────────────┐        │
                    │    │      V4L2 subdev 框架        │        │
                    │    │  (基于 Media Controller)     │        │
                    │    │                             │        │
                    │    │  ┌─────────┐ ┌─────────┐   │        │
                    │    │  │ CSIPHY  │ │  CSID   │   │        │
                    │    │  │ (物理层) │ │ (协议层) │   │        │
                    │    │  └─────────┘ └─────────┘   │        │
                    │    │  ┌─────────┐ ┌─────────┐   │        │
                    │    │  │ Sensor  │ │  CCI   │   │        │
                    │    │  │ (驱动)  │ │(I2C控制器)│   │        │
                    │    │  └─────────┘ └─────────┘   │        │
                    │    │  ┌─────────┐ ┌─────────┐   │        │
                    │    │  │ EEPROM  │ │Actuator│   │        │
                    │    │  │ (OTP)   │ │(AF马达) │   │        │
                    │    │  └─────────┘ └─────────┘   │        │
                    │    │  ┌─────────┐               │        │
                    │    │  │ Flash   │               │        │
                    │    │  └─────────┘               │        │
                    │    └───────────────────────────┘        │
                    └──────────────────────────────────────────┘
                                      │
                 ┌────────────────────┼────────────────────┐
                 ▼                    ▼                    ▼
             CSIPHY 硬件          Sensor 硬件          ISP (Spectra)
         (MIPI 物理层)        (通过 I2C/CCI 控制)    (影像信号处理器)

整个 Camera 数据路径

控制路径(控制 sensor 做什么):
  HAL → CSL → ioctl → CAMSS → I2C(CCI) → sensor 寄存器
  例如: "设置曝光时间 33ms" → I2C 写入 sensor 的曝光寄存器

数据路径(sensor 拍到的数据怎么回来):
  sensor → MIPI 差分信号 → CSIPHY → CSID → ISP → DDR → HAL → App

和老平台 msm_camera_v2 的区别

维度 msm_camera_v2(老平台) CAMSS(新平台)
适用平台 xxx 及之前 xxx 及之后
代码路径 kernel/drivers/media/.../ techpack/camera/
与主线关系 在 kernel 主线源码中 独立 repo,版本更灵活
Probe 行为 probe 时直接操作电源 probe 只登记,不上电
框架模型 V4L2 subdev + 自定义 ioctl V4L2 subdev + Media Controller
调度方式 单线程 多线程(cam-req-mgr 调度)

CAMSS 框架的文件组织

techpack/camera/
├── cam_req_mgr/       ← 请求调度器(核心调度模块)
├── csiphy/            ← MIPI 物理层
├── csid/              ← CSI 协议解码
├── sensor/            ← sensor 驱动
├── cci/               ← 相机 I2C 控制器
├── eeprom/            ← OTP 数据读取
├── actuator/          ← AF 马达驱动
└── flash/             ← 闪光灯驱动

18.2 V4L2 和 Media Controller——CAMSS 的基础

18.2.1 V4L2 是什么

V4L2(Video for Linux 2)是 Linux 内核中视频设备的标准接口框架。类比:

V4L2 之于摄像头  ≈  ALSA 之于音频  ≈  DRM 之于显示

高通没有使用标准 V4L2 的用户空间接口(/dev/videoX)。它只使用了 V4L2 的 subdev(子设备)模型——每个 camera 模块(CSIPHY、CSID、Sensor)都注册为一个 V4L2 subdev,通过 Media Controller 框架连接在一起。

18.2.2 Media Controller——摄像头模块的"连接图"

Media Controller 把每个硬件模块表示为一个 entity(实体),把模块之间的连接关系表示为 link(链接)。

Media Controller 视角下的 Camera 数据路径:

Sensor entity ──link──→ CSIPHY entity ──link──→ CSID entity ──link──→ IFE entity

在用户空间可以通过 media-ctl 工具查看这些连接:

adb shell media-ctl -p -d /dev/media0
# 输出类似:
# - entity 1: Sensor → [CSIPHY]
# - entity 2: CSIPHY → [CSID]
# - entity 3: CSID → [IFE]

18.3 CAMSS 各模块详解

数据从 sensor 到 ISP 的路径图

18.3.1 cam-req-mgr——请求调度器

CAMSS 的核心调度模块。HAL 通过 CSL 发出的每一个命令,都由 cam-req-mgr 协调各子设备执行。

HAL → ioctl() → cam-req-mgr → 查看命令类型
  ├── power up → 调用各子设备的 power_up 回调
  │               CSIPHY 先上电 → CSID 配置 → Sensor 初始化
  ├── stream on → 启动 MIPI 传输
  ├── set mode → 配置 sensor 输出分辨率/帧率
  │              通过 I2C 写入 sensor 寄存器
  └── power down → 逆序关电

18.3.2 CSIPHY——MIPI 物理层控制器

D-PHY(标准)     = MIPI 联盟定义的物理层串行标准(类似 Ethernet / PCIe 的物理层,规定电压电平、Lane 结构与速率)
CSIPHY(硬件实现) = 高通 SoC 内实现该标准的 PHY IP(负责从 MIPI 差分模拟信号中恢复时钟与数字数据)

D-PHY 是 MIPI 联盟定义的物理层通信标准,定义电压电平(1.2V 差分信号)、
Lane 结构(Clock + Data 各一对线)、速率(最高 2.5Gbps/Lane)。

CSIPHY 是高通 SoC 中的硬件 IP 模块,负责从 MIPI 模拟信号中恢复出数字数据。

输入: sensor 发来的高速差分信号(D-PHY 标准)
处理:
  ① 时钟恢复——从 Clock Lane 恢复出采样时钟
  ② 串并转换——把高速串行的数据转换成并行数据
  ③ Lane 合并——把多个 Data Lane 的数据合起来
输出: 并行 RAW 数据 → CSID

dtsi 配置: qcom,csiphy-sd-index = <0>;

常见问题:CSIPHY 的 Lane 速率或时序参数与 sensor 不匹配 → ECC 错误频繁 → 花屏。

18.3.3 CSID——CSI 协议解码器

CSID 解析 MIPI CSI-2 协议层的数据包。

MIPI CSI-2 的数据包结构:

Short Packet (帧同步): 帧头(FS) — 告诉 CSID "新的一帧到了"

Long Packet (图像数据):
  包头(PH) | RAW 数据(像素值) | 包尾(PF) | ECC 校验
  DataType 告诉 CSID: 这包数据是 RAW8(0x2A)、RAW10(0x2B)、还是 RAW12(0x2C)

输入: CSIPHY 传来的并行 RAW 数据(包含包头、数据、ECC)
处理:
  ① 数据包解析——识别 Short Packet 和 Long Packet
  ② Data Type 识别——判断是 RAW8/10/12
  ③ ECC/CRC 校验——检测传输错误
  ④ VC(Virtual Channel)解复用——多路数据分离
输出: 纯 RAW 像素数据 → ISP (IFE)

dtsi 配置: qcom,csid-sd-index = <0>;

常见问题:DataType 与 sensor 输出不匹配(如 sensor 输出 RAW10 但 CSID 配成 RAW12)→ ECC 错误频繁 → 花屏。

CSID 和 CSIPHY 的分工

Sensor → 差分信号 → CSIPHY → CSID → ISP (IFE)
                     │        │
                物理层信号   协议层解析
                时钟恢复    数据类型识别
                串并转换    ECC/CRC 校验

18.3.4 Sensor 驱动——控制 sensor 做什么

sensor 驱动是 CAMSS 中唯一需要你关注的子设备(CSIPHY/CSID 是 SoC 内部的,一般不需要动)。

Sensor 驱动的工作:
  ① probe 阶段(系统启动时):
     读取 dtsi 中的电源/GPIO/时钟配置
     通过 I2C 读一次 sensor 的 CHIP_ID 寄存器
     ID 匹配 → probe 成功;不匹配 → probe 失败

  ② 初始化阶段(HAL 触发):
     按照 dtsi 配置的电源 sequence 依次上电
     通过 I2C 写寄存器初始化 sensor

  ③ 运行阶段:
     接收 HAL 发来的曝光/增益/帧率设置
     通过 I2C 写入 sensor 的对应寄存器

  ④ 关闭阶段:
     逆序下电

Probe 日志示例:

// 成功:
[cam_sensor_probe] camera sensor probe success
sensor_id 0x0586 matched

// 失败(I2C 不通):
[cam_sensor_probe] i2c read failed, slave_addr 0x001a, err 121
// → 99% 电源没给或 RESET 没拉高

// 失败(ID 不匹配):
[cam_sensor_probe] sensor_id mismatch expected(0x0586) read(0x0000)

18.3.5 CCI——相机的 I2C 控制器

I2C 是一种低速串行通信协议(scl + sda 两根线),用于 CPU 和外围设备通信。

高通用的是 CCI(Camera Control Interface)——专门为 camera 优化的 I2C 控制器:
  - 支持更高速度(最高 1MHz,标准 I2C 只有 400KHz)
  - 支持批量写(一次 I2C 事务写入多个连续的 sensor 寄存器)
  - 有专用 DMA 通道(不占用 CPU)

18.4 dtsi 详解——kernel 是怎么知道硬件连接方式的

dtsi(Device Tree Source Include)描述了硬件在 SoC 上的连接方式。

从你的 xxx 源码 camera-devicetree/xxx-camera-sensor-qrd.dtsi

// Actuator(AF 马达)节点
actuator_rear: qcom,actuator0 {
    cell-index = <0>;
    compatible = "qcom,actuator";
    cci-master = <0>;
    cam_vaf-supply = <&pm8150a_l7>;
    regulator-names = "cam_vaf";
    rgltr-min-voltage = <2856000>;
    rgltr-max-voltage = <3104000>;
    rgltr-load-current = <100000>;
};

// Flash(闪光灯)节点
led_flash_rear: qcom,camera-flash0 {
    cell-index = <0>;
    compatible = "qcom,camera-flash";
    flash-source = <&pm8150l_flash0 &pm8150l_flash1>;
    torch-source = <&pm8150l_torch0 &pm8150l_torch1>;
    switch-source = <&pm8150l_switch2>;
};

dtsi 各字段速查:

compatible     = "qcom,actuator"    → 匹配驱动模块
cell-index     = <0>                → 模块编号(0=后摄)
cam_vaf-supply = <&pm8150a_l7>      → 电源来自 PMIC 的 LDO7 通道
cci-master     = <0>                → 使用 CCI 总线 0
flash-source   = <&pm8150l_flash0>  → 使用的闪光灯 LED 通道

18.5 CAMSS 的 Probe 流程

CAMSS 的

对照这个时序图看各阶段的先后顺序

系统启动时的 Probe

① 内核解析 dtsi → 找到各 camera 设备节点
② CSIPHY 注册 → 准备 D-PHY 配置接口
③ CSID 注册 → 准备 CSI 解码配置接口
④ Sensor probe → 读一次 I2C CHIP_ID 确认 sensor 存在
   ID 匹配 → probe success
   ID 不匹配 → probe failed
⑤ EEPROM / Actuator / Flash 注册

HAL open 之后的硬件激活

HAL 打开相机 → CSL → ioctl → CAMSS cam-req-mgr 调度:
   ① sensor 驱动执行上电序列:
      regulator_enable(cam_vana) → 1ms
      regulator_enable(cam_vio)  → 1ms
      clock_set_rate(mclk, 24MHz) → 1ms
      gpio_set_value(reset, 1)   → 5ms
      
   ② 通过 CCI (I2C) 写入 sensor 初始化寄存器序列
   ③ CSIPHY 配置 Lane 数量和速率
   ④ CSID 配置 Data Type
   ⑤ 所有就绪 → MIPI 传输开始 → ISP 接收

18.6 调试命令速查

# probe 状态
adb shell dmesg | grep -E "cam_sensor|sensor_id"

# 电源状态
adb shell cat /sys/kernel/debug/regulator/regulator_summary | grep -A 2 "cam"

# GPIO 状态
adb shell cat /sys/kernel/debug/gpio | grep -i "cam\|mclk\|reset"

# CCI/I2C 状态
adb shell cat /sys/kernel/debug/cci/status

# 错误日志
adb shell dmesg | grep -iE "fail|error|timeout|abort"

18.7 本章总结

CAMSS = xxx 上使用的 Camera SubSystem 驱动框架

模块速查:
  cam-req-mgr → 请求调度器,协调各子设备
  CSIPHY      → MIPI 物理层控制器
  CSID        → CSI 协议解码器 + ECC 校验
  Sensor      → I2C 控制 sensor 寄存器
  CCI         → 专用 I2C 控制器(比通用的快)
  EEPROM      → OTP 校准数据读取
  Actuator    → AF 马达控制
  Flash       → 闪光灯控制

sensor 工作的三个要素: 电源(regulator) + 时钟(GPIO) + I2C(寄存器)

排查: probe 失败 → I2C 不通 → 查电源/GPIO
       probe 成功但无数据 → MIPI 配置问题

动手验证

  1. 执行 adb shell dmesg | grep -E "cam_sensor|cam_csiphy|cam_csid",确认 probe 状态。
  2. 执行 adb shell cat /sys/kernel/debug/regulator/regulator_summary | grep -A 2 "cam",查看电源状态。
  3. camera-devicetree/xxx-camera-sensor-qrd.dtsi 中找到 actuator 节点,看它用了哪个 regulator。

常见误解