4. Sensor驱动初始化流程实战
Sensor 驱动能否被系统正确识别,几乎全部取决于 probe 函数这一段初始化流程是否严谨。联咏(Novatek)平台上的 Sensor 驱动通常挂在 I2C 总线上,由设备树或板级配置提供电源、时钟、复位和 PWDN 引脚信息。probe 函数被调用时,意味着内核已经匹配到设备节点,接下来要做的事情就是:把 Sensor 从“断电、复位、无时钟”的冷状态,一步步带到“I2C 可通信、ID 可读取”的可工作状态。本节围绕这条初始化链路展开,重点讲清楚每一步做什么、为什么这个顺序不能乱、以及如何验证。
一、probe 函数的整体职责与执行顺序
probe 函数不是简单地“打开电源然后读 ID”,它是一条有严格先后依赖的初始化链。典型顺序是:获取 GPIO 与 regulator 资源 → 使能电源(AVDD/DOVDD/DVDD)→ 使能 MCLK → 拉低复位、释放 PWDN → 等待稳定时间 → 建立 I2C 通信 → 读取并校验 Sensor ID → 注册 subdev 与回调。顺序错乱最常见的后果是 I2C 读不到 ACK,或者读回的 ID 是 0x00/0xFF。
下面这张图展示了联咏平台上 Sensor 从冷态到可通信状态的初始化信号链,包含电源、时钟、GPIO 与 I2C 之间的依赖关系:
二、电源与时钟管理
联咏平台一般通过 regulator 框架管理 Sensor 的多路电源。以常见的 2.8V/1.8V/1.2V 三路供电为例,probe 中需要先获取 regulator 句柄,再按顺序使能。典型代码结构如下:
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sensor_priv *priv;
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->avdd = devm_regulator_get(&client->dev, "avdd");
priv->dovdd = devm_regulator_get(&client->dev, "dovdd");
priv->dvdd = devm_regulator_get(&client->dev, "dvdd");
ret = regulator_enable(priv->avdd);
if (ret) return ret;
ret = regulator_enable(priv->dovdd);
if (ret) goto err_avdd;
ret = regulator_enable(priv->dvdd);
if (ret) goto err_dovdd;
priv->mclk = devm_clk_get(&client->dev, "mclk");
ret = clk_prepare_enable(priv->mclk);
if (ret) goto err_dvdd;
msleep(5); /* 等待电源与时钟稳定 */
...
}
这里有两个容易踩坑的点。第一,regulator 使能顺序建议先 AVDD 再 DOVDD 最后 DVDD,部分 Sensor 对 DVDD 先于 AVDD 上电敏感,可能触发内部 ESD 保护。第二,MCLK 必须在电源稳定后再使能,且频率要与 Sensor 规格一致,常见为 24MHz。若时钟频率偏差过大,I2C 虽然可能通信成功,但后续出图会出现花屏或帧率异常。
三、GPIO 复位与 PWDN 控制
复位(RESET)和掉电(PWDN)是两个独立引脚,但常常被混淆。RESET 用于让 Sensor 内部寄存器回到默认值,PWDN 用于让 Sensor 进入低功耗待机。probe 中的标准动作是:先将 RESET 拉低、PWDN 拉低(或按手册要求拉高),保持一段时间后释放 RESET,再释放 PWDN。
priv->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
priv->pwdn_gpio = devm_gpiod_get(&client->dev, "pwdn", GPIOD_OUT_LOW);
gpiod_set_value(priv->reset_gpio, 0);
gpiod_set_value(priv->pwdn_gpio, 0);
msleep(2);
gpiod_set_value(priv->reset_gpio, 1); /* 释放复位 */
msleep(1);
gpiod_set_value(priv->pwdn_gpio, 1); /* 退出掉电 */
msleep(5);
注意 PWDN 的有效电平因 Sensor 而异,有的高有效、有的低有效,必须查数据手册确认。如果 PWDN 一直处于有效状态,I2C 从机会被关闭,表现为读地址无 ACK。复位保持时间一般要求 ≥ 1ms,释放后到第一次 I2C 访问之间建议再等 1~5ms,给内部 PLL 锁定留出时间。
四、I2C 通信验证与 Sensor ID 读取
完成电源、时钟、GPIO 后,就可以验证 I2C 通信了。联咏平台常用 i2c_transfer 或 regmap 封装读写。最直接的验证方式是读取 Sensor 的 ID 寄存器。以某款常见 Sensor 为例,ID 寄存器地址为 0x0000,高字节应为 0x56,低字节为 0x40。读取代码如下:
static int sensor_read_id(struct sensor_priv *priv, u16 *id)
{
u8 buf[2];
struct i2c_msg msgs[2];
u8 reg_addr[2] = {0x00, 0x00};
msgs[0].addr = priv->client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = reg_addr;
msgs[1].addr = priv->client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 2;
msgs[1].buf = buf;
if (i2c_transfer(priv->client->adapter, msgs, 2) != 2)
return -EIO;
*id = (buf[0] << 8) | buf[1];
return 0;
}
读回 ID 后必须做校验,而不是打印一下就算完成。校验逻辑通常是:先判断读操作是否成功,再判断 ID 是否等于预期值。如果 ID 为 0x0000 或 0xFFFF,说明 I2C 没有真正通信成功,需要回头检查地址、上拉电阻、电源和 PWDN 状态。如果 ID 是其他值,可能是地址冲突或读到了别的器件。
五、失败回滚与资源释放
probe 中任何一步失败,都必须回滚前面已经使能的资源,否则会造成电源常开、时钟泄漏,甚至影响其他模块。推荐使用 goto 标签逐级回滚,顺序与使能顺序相反:先关 MCLK,再依次关 DVDD、DOVDD、AVDD。GPIO 如果使用了 devm 管理,通常无需手动释放,但显式拉回复位状态更稳妥。
六、probe 完成后的注册动作
ID 校验通过后,probe 还需要完成 v4l2 subdev 的注册、设置 sensor 的操作函数集(s_stream、s_fmt、s_power 等),并把 priv 挂到 client 的 drvdata 上。至此,Sensor 才真正对上层 ISP 可见。后续的曝光、增益、帧率配置,都建立在这一步成功的基础之上。
把整条链路串起来看,probe 的本质是“按依赖顺序把硬件唤醒,并用 ID 读回作为最终验收”。电源、时钟、GPIO、I2C 四者缺一不可,顺序和等待时间是最容易被忽视却最影响成功率的细节。实际调试时,建议先确保 ID 能稳定读回,再去调图像效果,否则会在错误的方向上浪费大量时间。