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 传输,提供寄存器读写 APIi2c_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 多用于高帧率或需要大量寄存器批量写入的场景。两种接口在驱动中的实现差异如下:

对比项I2CSPI
总线速率100 kHz – 1 MHz10 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_recvspi_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 的上电时序参数表:

步骤操作最小延迟说明
1DVDD 上电0 μs先于或同时于 AVDD
2AVDD 上电100 μsDVDD 稳定后
3IOVDD 上电100 μsAVDD 稳定后
4XSHUTDOWN 拉高1 ms所有电源稳定后
5I2C 通信开始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 流开启的完整电源与初始化序列:

Camera Sensor 驱动电源与初始化序列 Camera Sensor 驱动电源与初始化序列 驱动 Probe / 设备匹配 注册 regmap / 读取 Chip ID 验证 解析 DTS:regulator / gpio / 时钟 上电序列:DVDD → AVDD → IOVDD XSHUTDOWN 拉高 (延迟 1ms) 等待 PLL 锁定 (10ms) 寄存器初始化 / regcache_sync MIPI CSI 流开启 (stream on) 电源管理关键点 • 严格时序不可省略 • 使用 usleep_range • 异常路径需 power down

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 后问题解决。