4. Camera Sensor 驱动模型:I2C/SPI 控制接口、寄存器读写、电源管理
在骁龙平台 MIPI CSI 链路中,Camera Sensor 是图像数据的源头。Sensor 驱动并非直接操作 MIPI D-PHY 物理层,而是通过 I2C 或 SPI 控制总线 完成寄存器配置、流控切换与电源状态管理。本章深入 Sensor 驱动的三层抽象模型,并给出骁龙平台下的移植要点。
4.1 驱动模型分层架构
现代 Camera Sensor 驱动遵循 Linux 内核 V4L2 子系统的分层设计,从上到下依次为:
| 层次 | 职责 | 关键接口/结构体 |
|---|---|---|
| V4L2 子设备层 | 注册为 V4L2 subdev,提供 pad-level 操作 | v4l2_subdev_ops、media_entity_ops |
| 控制总线抽象层 | 封装 I2C/SPI 传输,提供寄存器读写 API | i2c_client、spi_device、regmap |
| 电源管理序列层 | 管理 XSHUTDOWN、DVDD/AVDD/IOVDD 时序 | gpiod_set_value、regulator_bulk_enable |
| 硬件寄存器层 | Sensor 内部寄存器地址与参数表 | 厂商 Datasheet 寄存器映射表 |
移植要点: 骁龙平台通常使用
cam_sensor 驱动框架,该框架在 V4L2 subdev 之上增加了 OIS / EEPROM / actuator 的复合设备管理。移植时需关注 cam_sensor_core.c 中的 sensor_power_up 和 sensor_power_down 序列。
4.2 I2C / SPI 控制接口实现
Sensor 控制总线以 I2C 最为常见,SPI 多用于高帧率或需要大量寄存器批量写入的场景。两种接口在驱动中的实现差异如下:
| 对比项 | I2C | SPI |
|---|---|---|
| 总线速率 | 100 kHz – 1 MHz | 10 MHz – 50 MHz |
| 地址宽度 | 7-bit / 10-bit 设备地址 + 8/16-bit 寄存器地址 | 无设备地址,片选 CS 选择设备 |
| 传输模式 | SMBus block / I2C combined write-then-read | 全双工,MOSI/MISO 同时传输 |
| 驱动接口 | i2c_master_send / i2c_master_recv | spi_write_then_read / spi_sync_transfer |
| 批量写入效率 | 每字节需 ACK,效率较低 | 连续时钟,效率高 |
4.2.1 寄存器读写封装示例
以下代码展示基于 regmap 的 I2C 寄存器读写封装,该方式可自动处理缓存、字节序和批量操作:
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16, // 寄存器地址 16 位
.val_bits = 8, // 寄存器值 8 位
.max_register = 0xffff,
.cache_type = REGCACHE_RBTREE,
};
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
struct sensor_device *sensor;
regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
sensor->regmap = regmap;
sensor->client = client;
// 读取芯片 ID 验证
regmap_read(regmap, 0x0000, &sensor->chip_id);
if (sensor->chip_id != 0x1234) {
dev_err(&client->dev, "chip ID mismatch: 0x%04x\n", sensor->chip_id);
return -ENODEV;
}
return 0;
}
骁龙平台差异: 部分骁龙 SoC 的 I2C 控制器存在 FIFO 深度限制(如 32 字节),批量写入超过 FIFO 深度时需拆分为多次传输,否则会导致 NACK 或总线挂死。建议在 probe 阶段通过
i2c_check_functionality 检测 I2C_FUNC_I2C 标志。
4.3 电源管理序列
Camera Sensor 的电源管理是驱动移植中最容易出问题的环节。典型的 Sensor 需要三路电源:
- DVDD (Digital Core):1.2V – 1.8V,数字核心供电
- AVDD (Analog):2.8V – 3.3V,模拟电路供电
- IOVDD (I/O):1.8V – 2.8V,I2C/SPI 接口电平
此外,XSHUTDOWN(硬件复位/使能引脚)的时序必须严格遵循 Datasheet 要求。
4.3.1 上电时序要求
以下是一个典型 Sensor 的上电时序参数表:
| 步骤 | 操作 | 最小延迟 | 说明 |
|---|---|---|---|
| 1 | DVDD 上电 | 0 μs | 先于或同时于 AVDD |
| 2 | AVDD 上电 | 100 μs | DVDD 稳定后 |
| 3 | IOVDD 上电 | 100 μs | AVDD 稳定后 |
| 4 | XSHUTDOWN 拉高 | 1 ms | 所有电源稳定后 |
| 5 | I2C 通信开始 | 10 ms | 等待内部 PLL 锁定 |
常见错误: 部分移植工程师将 XSHUTDOWN 与电源使能同时拉高,导致 Sensor 在电源未稳定时进入内部复位状态,表现为 I2C 通信超时或 MIPI 时钟无法锁定。务必在驱动中通过
usleep_range 插入精确延迟。
4.3.2 电源管理代码结构
static int sensor_power_up(struct sensor_device *sensor)
{
int ret;
// 1. 使能 DVDD
ret = regulator_enable(sensor->dvdd);
if (ret) return ret;
usleep_range(100, 200);
// 2. 使能 AVDD
ret = regulator_enable(sensor->avdd);
if (ret) goto disable_dvdd;
usleep_range(100, 200);
// 3. 使能 IOVDD
ret = regulator_enable(sensor->iovdd);
if (ret) goto disable_avdd;
usleep_range(100, 200);
// 4. XSHUTDOWN 拉高
gpiod_set_value_cansleep(sensor->xshutdown, 1);
usleep_range(1000, 2000);
// 5. 等待 PLL 稳定
msleep(10);
return 0;
disable_avdd:
regulator_disable(sensor->avdd);
disable_dvdd:
regulator_disable(sensor->dvdd);
return ret;
}
static int sensor_power_down(struct sensor_device *sensor)
{
gpiod_set_value_cansleep(sensor->xshutdown, 0);
usleep_range(100, 200);
regulator_disable(sensor->iovdd);
regulator_disable(sensor->avdd);
regulator_disable(sensor->dvdd);
return 0;
}
4.4 电源状态与寄存器上下文保存
在系统休眠(suspend)或 Sensor 流关闭(stream off)时,部分 Sensor 会丢失寄存器配置。驱动需要实现 上下文保存与恢复 机制:
- 上下文保存:在 power_down 前,通过批量读取将关键寄存器(曝光、增益、帧大小、MIPI 配置)存入内存。
- 上下文恢复:在 power_up 且 XSHUTDOWN 释放后,按顺序写入保存的寄存器值。
优化建议: 对于骁龙平台,建议使用
regmap 的 regcache_sync 功能。在 sensor_power_down 中调用 regcache_mark_dirty,在 sensor_power_up 中调用 regcache_sync,可自动恢复所有缓存寄存器,避免手动维护寄存器列表。
4.5 驱动模型与电源管理流程图
下图展示了从驱动加载到 MIPI 流开启的完整电源与初始化序列:
4.6 骁龙平台移植避坑清单
- I2C 地址冲突: 骁龙平台 Camera 子系统中,多个 Sensor 可能共享同一条 I2C 总线。务必确认每个 Sensor 的 7-bit 地址在 DTS 中正确配置,且不与 EEPROM / OIS 驱动冲突。
- XSHUTDOWN GPIO 电平反转: 部分 Sensor 的 XSHUTDOWN 为低电平有效,DTS 中需设置
gpio-enable-active-high或gpio-enable-active-low属性。 - MCLK 频率匹配: Sensor 的 master clock(MCLK)通常为 24 MHz 或 27 MHz。骁龙平台通过
camcc时钟控制器提供,需在 DTS 中指定clocks和clock-frequency。 - regmap 缓存失效: 如果 Sensor 在 power_down 后完全掉电,regmap 的 cache 必须标记为 dirty,否则恢复时写入的寄存器值可能无效。
实战案例: 某项目移植 IMX586 驱动时,发现上电后 I2C 读取 Chip ID 返回 0xFF。经排查,XSHUTDOWN 拉高后仅延迟 100 μs 就开始通信,而 Sensor 内部 PLL 需要 8 ms 稳定。将延迟修正为 10 ms 后问题解决。