前面 17 章全在用户空间——App、Framework、HAL。这一章跨进内核空间。
HAL 层通过 CSL 调用 ioctl 与内核驱动通信。当你调了 CSLPowerUp(),最终会通过系统调用进入内核,由内核驱动去操作硬件——拉高 GPIO、使能 regulator、通过 I2C 配置 sensor 寄存器。
这一章不讲"怎么写一个 kernel 驱动"——而是讲这个框架长什么样、由哪些模块组成、数据怎么在模块之间流转、出了问题怎么查。

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(老平台) | CAMSS(新平台) |
|---|---|---|
| 适用平台 | xxx 及之前 | xxx 及之后 |
| 代码路径 | kernel/drivers/media/.../ |
techpack/camera/ |
| 与主线关系 | 在 kernel 主线源码中 | 独立 repo,版本更灵活 |
| Probe 行为 | probe 时直接操作电源 | probe 只登记,不上电 |
| 框架模型 | V4L2 subdev + 自定义 ioctl | V4L2 subdev + Media Controller |
| 调度方式 | 单线程 | 多线程(cam-req-mgr 调度) |
techpack/camera/
├── cam_req_mgr/ ← 请求调度器(核心调度模块)
├── csiphy/ ← MIPI 物理层
├── csid/ ← CSI 协议解码
├── sensor/ ← sensor 驱动
├── cci/ ← 相机 I2C 控制器
├── eeprom/ ← OTP 数据读取
├── actuator/ ← AF 马达驱动
└── flash/ ← 闪光灯驱动
V4L2(Video for Linux 2)是 Linux 内核中视频设备的标准接口框架。类比:
V4L2 之于摄像头 ≈ ALSA 之于音频 ≈ DRM 之于显示
高通没有使用标准 V4L2 的用户空间接口(/dev/videoX)。它只使用了 V4L2 的 subdev(子设备)模型——每个 camera 模块(CSIPHY、CSID、Sensor)都注册为一个 V4L2 subdev,通过 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]

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 → 逆序关电
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 错误频繁 → 花屏。
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 校验
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)
I2C 是一种低速串行通信协议(scl + sda 两根线),用于 CPU 和外围设备通信。
高通用的是 CCI(Camera Control Interface)——专门为 camera 优化的 I2C 控制器:
- 支持更高速度(最高 1MHz,标准 I2C 只有 400KHz)
- 支持批量写(一次 I2C 事务写入多个连续的 sensor 寄存器)
- 有专用 DMA 通道(不占用 CPU)
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 通道


① 内核解析 dtsi → 找到各 camera 设备节点
② CSIPHY 注册 → 准备 D-PHY 配置接口
③ CSID 注册 → 准备 CSI 解码配置接口
④ Sensor probe → 读一次 I2C CHIP_ID 确认 sensor 存在
ID 匹配 → probe success
ID 不匹配 → probe failed
⑤ EEPROM / Actuator / Flash 注册
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 接收
# 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"
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 配置问题
adb shell dmesg | grep -E "cam_sensor|cam_csiphy|cam_csid",确认 probe 状态。adb shell cat /sys/kernel/debug/regulator/regulator_summary | grep -A 2 "cam",查看电源状态。camera-devicetree/xxx-camera-sensor-qrd.dtsi 中找到 actuator 节点,看它用了哪个 regulator。