4. Sensor 驱动初始化流程:probe 函数、电源管理、时钟配置、复位时序
Sensor 驱动的初始化是整个 Camera 子系统正常工作的基石。在 RK 平台上,Sensor 驱动通常遵循 Linux 内核 V4L2 子设备驱动模型,其核心入口是 probe 函数。本章将深入剖析从设备树匹配到 Sensor 输出第一帧数据的完整初始化链路,重点拆解电源管理、时钟配置与复位时序这三个关键环节。
4.1 probe 函数:驱动的入口与设备树匹配
当内核检测到 I2C 总线上存在与设备树(Device Tree)匹配的 Sensor 节点时,会调用驱动中注册的 probe 函数。该函数负责完成驱动的初始化、资源申请以及子设备的注册。
4.1.1 probe 函数的标准流程
- 获取设备树资源: 解析 Sensor 节点,获取 GPIO(复位、电源使能、中断)、时钟频率、电源电压、I2C 地址等配置。
- 初始化 I2C 客户端: 验证 I2C 通信是否正常,通常通过读取 Sensor 的 Chip ID 寄存器来确认。
- 分配并初始化驱动私有数据结构: 创建
struct sensor_priv结构体,用于存储运行时状态。 - 配置电源与时钟: 调用电源管理函数和时钟配置函数,确保 Sensor 处于工作状态。
- 执行硬件复位: 按照 Sensor 数据手册的时序要求,拉低/拉高复位引脚。
- 注册 V4L2 子设备: 调用
v4l2_i2c_subdev_init或v4l2_async_register_subdev将 Sensor 注册到 V4L2 框架中。
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 位 |
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 通信开始的完整时序关系:
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;
}
4.6 多 Camera 场景下的初始化差异
在多 Camera 系统中,每个 Sensor 的初始化流程基本相同,但需要注意以下差异点:
| 差异项 | 单 Camera | 多 Camera |
|---|---|---|
| 设备树节点 | 单个 I2C 节点 | 多个 I2C 节点,可能分布在不同的 I2C 总线 |
| 电源域 | 独立电源 | 可能共享电源域,需考虑电源隔离 |
| 时钟源 | 独占时钟 | 可能共享 MCLK,需确保时钟驱动能力 |
| 复位 GPIO | 单个 GPIO | 每个 Sensor 独立复位 GPIO,避免相互影响 |
| I2C 地址 | 唯一地址 | 每个 Sensor 必须拥有唯一的 I2C 地址 |
通过本章的学习,你应该能够独立完成 RK 平台 Sensor 驱动的 probe 函数编写,并正确处理电源管理、时钟配置和复位时序这三个关键环节。这些基础能力将直接决定后续 Sensor 图像数据采集的成败。