4. Sensor 驱动初始化流程:probe 函数、电源管理、时钟配置、复位时序

Sensor 驱动的初始化是整个 Camera 子系统正常工作的基石。在 RK 平台上,Sensor 驱动通常遵循 Linux 内核 V4L2 子设备驱动模型,其核心入口是 probe 函数。本章将深入剖析从设备树匹配到 Sensor 输出第一帧数据的完整初始化链路,重点拆解电源管理、时钟配置与复位时序这三个关键环节。

4.1 probe 函数:驱动的入口与设备树匹配

当内核检测到 I2C 总线上存在与设备树(Device Tree)匹配的 Sensor 节点时,会调用驱动中注册的 probe 函数。该函数负责完成驱动的初始化、资源申请以及子设备的注册。

核心职责: probe 函数是 Sensor 驱动的“构造函数”,它必须完成从硬件资源获取到 V4L2 子设备注册的全过程。

4.1.1 probe 函数的标准流程

  1. 获取设备树资源: 解析 Sensor 节点,获取 GPIO(复位、电源使能、中断)、时钟频率、电源电压、I2C 地址等配置。
  2. 初始化 I2C 客户端: 验证 I2C 通信是否正常,通常通过读取 Sensor 的 Chip ID 寄存器来确认。
  3. 分配并初始化驱动私有数据结构: 创建 struct sensor_priv 结构体,用于存储运行时状态。
  4. 配置电源与时钟: 调用电源管理函数和时钟配置函数,确保 Sensor 处于工作状态。
  5. 执行硬件复位: 按照 Sensor 数据手册的时序要求,拉低/拉高复位引脚。
  6. 注册 V4L2 子设备: 调用 v4l2_i2c_subdev_init 或 v4l2_async_register_subdev 将 Sensor 注册到 V4L2 框架中。
经验提示: 在 RK 平台上,Chip ID 校验失败是最常见的 probe 失败原因。建议在 probe 函数中增加重试机制,并打印详细的 I2C 通信日志。

4.1.2 设备树匹配与数据提取

设备树节点是驱动与硬件配置的桥梁。以下是一个典型的 RK 平台 Sensor 设备树节点示例:

// 设备树节点示例 (rk3588-camera.dtsi)
&i2c4 {
    status = "okay";
    imx415: imx415@1a {
        compatible = "sony,imx415";
        reg = <0x1a>;
        clocks = <&cru CLK_MIPI_CAMERA>;
        clock-names = "xvclk";
        pinctrl-names = "default";
        pinctrl-0 = <&camera_pins>;
        power-domains = <&power RK3588_PD_VI>;
        avdd-supply = <&vcc_camera_avdd>;    // 模拟电源 2.8V
        dvdd-supply = <&vcc_camera_dvdd>;    // 数字电源 1.2V
        iovdd-supply = <&vcc_camera_iovdd>;  // IO 电源 1.8V
        reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = <0>; // 0: back, 1: front
    };
};

驱动中通过 of_property_read_u32、devm_clk_get、devm_regulator_get 等 API 提取这些配置。

4.2 电源管理:多路电源的上电时序

现代 Sensor 通常需要多路电源(AVDD、DVDD、IOVDD),且各路电源的上电顺序和稳定时间有严格要求。错误的电源时序可能导致 Sensor 无法初始化,甚至永久损坏。

4.2.1 电源域与 regulator 框架

RK 平台使用 Linux 内核的 regulator 框架管理 Sensor 电源。驱动通过设备树中定义的 *-supply 属性获取 regulator 句柄,并控制其开关。

电源名称 典型电压 主要用途 上电顺序
AVDD 2.8V 模拟电路供电(像素阵列、ADC) 第 1 位
DVDD 1.2V / 1.8V 数字核心供电(逻辑、ISP) 第 2 位
IOVDD 1.8V I/O 接口供电(I2C、MIPI、GPIO) 第 3 位
关键约束: 部分 Sensor 要求 AVDD 必须先于 DVDD 稳定,且 IOVDD 必须在 DVDD 之后上电。务必查阅 Sensor 数据手册的“Power-Up Sequence”章节。

4.2.2 电源管理代码实现

驱动中通常封装一个 sensor_power_on 函数,按序使能各路电源:

static int sensor_power_on(struct device *dev) {
    struct sensor_priv *priv = dev_get_drvdata(dev);
    int ret;

    // 1. 使能 AVDD (2.8V)
    ret = regulator_enable(priv->avdd_reg);
    if (ret) goto err_avdd;
    usleep_range(1000, 2000); // 等待 1-2ms 稳定

    // 2. 使能 DVDD (1.2V)
    ret = regulator_enable(priv->dvdd_reg);
    if (ret) goto err_dvdd;
    usleep_range(1000, 2000);

    // 3. 使能 IOVDD (1.8V)
    ret = regulator_enable(priv->iovdd_reg);
    if (ret) goto err_iovdd;
    usleep_range(5000, 10000); // IO 电源稳定时间通常更长

    // 4. 等待所有电源稳定
    msleep(10);
    return 0;

err_iovdd:
    regulator_disable(priv->dvdd_reg);
err_dvdd:
    regulator_disable(priv->avdd_reg);
err_avdd:
    return ret;
}

4.3 时钟配置:MCLK 与 PCLK 的供给

Sensor 需要外部主时钟(MCLK / XVCLK)来驱动内部 PLL 和像素时钟。RK 平台通过 CRU(Clock & Reset Unit)提供可配置的时钟源。

4.3.1 时钟树与频率选择

Sensor 的时钟频率直接影响帧率和图像质量。常见的 MCLK 频率包括 24MHz、27MHz、37.125MHz 等。驱动需要根据设备树中配置的时钟频率,调用 clk_set_rate 设置正确的频率。

static int sensor_clk_config(struct sensor_priv *priv) {
    int ret;

    // 获取时钟句柄
    priv->xvclk = devm_clk_get(priv->dev, "xvclk");
    if (IS_ERR(priv->xvclk)) {
        dev_err(priv->dev, "failed to get xvclk\n");
        return PTR_ERR(priv->xvclk);
    }

    // 设置时钟频率为 24MHz
    ret = clk_set_rate(priv->xvclk, 24000000);
    if (ret) {
        dev_err(priv->dev, "failed to set xvclk rate\n");
        return ret;
    }

    // 使能时钟
    ret = clk_prepare_enable(priv->xvclk);
    if (ret) {
        dev_err(priv->dev, "failed to enable xvclk\n");
        return ret;
    }

    return 0;
}
调试技巧: 使用 cat /sys/kernel/debug/clk/clk_summary 可以查看当前时钟树中各时钟的使能状态和频率,是排查时钟问题的利器。

4.4 复位时序:硬件初始化的最后一步

在电源和时钟稳定后,必须对 Sensor 执行硬件复位,使其内部状态机进入已知的初始状态。复位时序的准确性直接决定 Sensor 能否正常响应 I2C 命令。

4.4.1 复位时序要求

不同 Sensor 的复位时序差异较大,但通常遵循以下通用模式:

  • 复位低电平保持时间: 至少 1ms 以上,确保内部电路完全放电。
  • 复位释放后的稳定时间: 拉高复位引脚后,需要等待 5-20ms,让 Sensor 完成内部初始化。
  • I2C 通信起始时间: 必须在复位释放且稳定时间过后,才能发起首次 I2C 访问。
static int sensor_hardware_reset(struct sensor_priv *priv) {
    // 拉低复位引脚 (假设低电平有效)
    gpiod_set_value_cansleep(priv->reset_gpio, 0);
    msleep(5); // 保持低电平 5ms

    // 拉高复位引脚,释放复位
    gpiod_set_value_cansleep(priv->reset_gpio, 1);
    msleep(20); // 等待 Sensor 内部初始化完成

    return 0;
}

4.4.2 复位时序的完整流程

下图展示了从电源上电到 I2C 通信开始的完整时序关系:

Sensor 上电与复位时序图 AVDD DVDD IOVDD MCLK RESET t0 t1 t2 t3 t4 t5 t6 时序说明 t0-t1: AVDD 上电 t1-t2: DVDD 上电 t2-t3: IOVDD 上电 t3-t4: MCLK 稳定 t4-t6: 复位脉冲

4.5 初始化流程的完整调用链

将上述所有步骤串联起来,形成 Sensor 驱动的完整初始化调用链:

static int sensor_probe(struct i2c_client *client,
                        const struct i2c_device_id *id) {
    struct sensor_priv *priv;
    int ret;

    // 1. 分配私有数据结构
    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    i2c_set_clientdata(client, priv);

    // 2. 解析设备树资源
    ret = sensor_parse_dt(priv);
    if (ret) return ret;

    // 3. 配置电源 (按序使能 AVDD, DVDD, IOVDD)
    ret = sensor_power_on(priv->dev);
    if (ret) return ret;

    // 4. 配置时钟 (设置频率并使能)
    ret = sensor_clk_config(priv);
    if (ret) goto err_clk;

    // 5. 执行硬件复位
    ret = sensor_hardware_reset(priv);
    if (ret) goto err_reset;

    // 6. 验证 I2C 通信 (读取 Chip ID)
    ret = sensor_check_chip_id(priv);
    if (ret) goto err_id;

    // 7. 初始化 V4L2 子设备
    v4l2_i2c_subdev_init(&priv->sd, client, &sensor_subdev_ops);
    priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;

    // 8. 注册异步子设备
    ret = v4l2_async_register_subdev(&priv->sd);
    if (ret) goto err_async;

    dev_info(&client->dev, "Sensor probe success\n");
    return 0;

err_async:
err_id:
err_reset:
    clk_disable_unprepare(priv->xvclk);
err_clk:
    sensor_power_off(priv->dev);
    return ret;
}
错误处理原则: 初始化链中的任何一步失败,都必须执行逆序的清理操作(错误路径回滚),避免资源泄漏导致后续 probe 失败。

4.6 多 Camera 场景下的初始化差异

在多 Camera 系统中,每个 Sensor 的初始化流程基本相同,但需要注意以下差异点:

差异项 单 Camera 多 Camera
设备树节点 单个 I2C 节点 多个 I2C 节点,可能分布在不同的 I2C 总线
电源域 独立电源 可能共享电源域,需考虑电源隔离
时钟源 独占时钟 可能共享 MCLK,需确保时钟驱动能力
复位 GPIO 单个 GPIO 每个 Sensor 独立复位 GPIO,避免相互影响
I2C 地址 唯一地址 每个 Sensor 必须拥有唯一的 I2C 地址
实战建议: 在多 Camera 项目中,建议为每个 Sensor 创建独立的驱动实例,并在设备树中明确区分电源、时钟和复位资源。避免在驱动中硬编码任何与硬件拓扑相关的假设。

通过本章的学习,你应该能够独立完成 RK 平台 Sensor 驱动的 probe 函数编写,并正确处理电源管理、时钟配置和复位时序这三个关键环节。这些基础能力将直接决定后续 Sensor 图像数据采集的成败。