3. RK平台Sensor驱动源码结构:probe、remove、power_on/off、s_stream回调函数

RK平台Sensor驱动的核心骨架由四个关键回调函数构成:probe(探测与初始化)、remove(卸载与资源释放)、power_on/off(电源与上下电时序管理)以及 s_stream(数据流启停控制)。理解这四个回调函数的职责、调用顺序与实现要点,是掌握Sensor驱动调试的基础。

核心认知:在RK平台V4L2子系统中,Sensor驱动本质上是v4l2_subdev的实例化。probe完成设备注册与硬件初始化,remove做逆向清理,power_on/off管理物理电源与时钟,s_stream控制MIPI数据流与帧同步。这四个函数构成了驱动生命周期管理的完整闭环。

3.1 probe回调函数:驱动的入口与初始化

probe函数在设备与驱动匹配时被内核调用,是Sensor驱动的初始化入口。其主要职责包括:

  • 设备树解析:读取reg(I2C地址)、clocks(时钟源)、power-domains(电源域)、reset-gpios(复位引脚)等属性。
  • I2C客户端初始化:通过i2c_check_functionality验证适配器能力,设置客户端私有数据。
  • V4L2子设备注册:调用v4l2_i2c_subdev_init或v4l2_subdev_init初始化v4l2_subdev结构体,并绑定回调函数集。
  • 媒体实体初始化:设置media_entity的pad数量与类型(通常为1个源pad)。
  • 电源与时钟获取:通过devm_clk_get获取MCLK,通过devm_regulator_bulk_get获取多路电源。
  • 芯片ID校验:读取Sensor的CHIP_ID寄存器,验证硬件是否匹配。
调试要点:probe失败最常见的原因是设备树节点与驱动不匹配(compatible字符串错误)或I2C通信异常。建议在probe入口处添加dev_info打印,确认函数被调用;在读取CHIP_ID后立即打印寄存器值,快速定位硬件通信问题。
static int sensor_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct sensor_priv *priv;
    int ret;

    dev_info(dev, "sensor probe start\n");

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

    /* 2. 解析设备树 */
    ret = sensor_parse_dt(dev, priv);
    if (ret)
        return ret;

    /* 3. 获取电源与时钟 */
    priv->xvclk = devm_clk_get(dev, "xvclk");
    if (IS_ERR(priv->xvclk))
        return PTR_ERR(priv->xvclk);

    /* 4. 上电并读取芯片ID */
    sensor_power_on(priv);
    ret = sensor_check_chip_id(priv);
    sensor_power_off(priv);
    if (ret)
        return ret;

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

    /* 6. 注册媒体实体pad */
    priv->pad.flags = MEDIA_PAD_FL_SOURCE;
    ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad);
    if (ret)
        return ret;

    dev_info(dev, "sensor probe success, chip_id=0x%04x\n", priv->chip_id);
    return 0;
}
probe回调函数执行流程图 probe 入口 解析设备树属性 获取时钟/电源/GPIO 上电 → 读取CHIP_ID V4L2子设备初始化 媒体实体pad注册 probe 成功返回0 CHIP_ID不匹配 失败 返回 -ENODEV

3.2 remove回调函数:资源的逆向释放

remove函数在驱动卸载或设备移除时被调用,负责释放probe阶段申请的所有资源。其典型实现包括:

  • 调用media_entity_cleanup清理媒体实体。
  • 调用v4l2_device_unregister_subdev注销子设备。
  • 调用sensor_power_off确保设备处于下电状态。
  • 释放GPIO、中断、DMA缓冲区等硬件资源。
常见陷阱:remove函数中必须检查设备是否处于streaming状态。如果s_stream正在运行,应先调用sensor_s_stream(0)停止数据流,再执行资源释放,否则可能导致内核崩溃或DMA访问非法内存。
static int sensor_remove(struct i2c_client *client)
{
    struct sensor_priv *priv = i2c_get_clientdata(client);

    /* 1. 确保数据流已停止 */
    if (priv->streaming)
        sensor_s_stream(&priv->subdev, 0);

    /* 2. 下电 */
    sensor_power_off(priv);

    /* 3. 清理媒体实体 */
    media_entity_cleanup(&priv->subdev.entity);

    /* 4. 注销V4L2子设备 */
    v4l2_device_unregister_subdev(&priv->subdev);

    dev_info(&client->dev, "sensor removed\n");
    return 0;
}

3.3 power_on/off回调函数:电源与时序管理

power_on和power_off函数通常不是V4L2子设备操作集的直接成员,而是由驱动内部封装,供probe、s_stream以及系统休眠唤醒时调用。在RK平台上,Sensor的上下电时序需要严格遵循硬件手册,否则可能导致初始化失败或图像异常。

电源操作 典型执行顺序 关键注意事项
power_on 1. 使能AVDD(模拟电源)→ 2. 使能DOVDD(数字IO电源)→ 3. 使能DVDD(数字核心电源)→ 4. 使能MCLK(主时钟)→ 5. 释放复位(RST低→高)→ 6. 等待Tready(稳定时间) 各电源之间通常需要1~5ms延迟;MCLK频率必须与Sensor规格一致(常见24MHz/27MHz);复位释放后需等待至少10ms才能进行I2C通信。
power_off 1. 拉低复位(RST高→低)→ 2. 关闭MCLK → 3. 关闭DVDD → 4. 关闭DOVDD → 5. 关闭AVDD 下电顺序与上电相反;关闭电源前必须确保I2C总线空闲;部分Sensor需要先写入shutdown寄存器再断电。
static int sensor_power_on(struct sensor_priv *priv)
{
    struct device *dev = &priv->subdev.dev;
    int ret;

    /* 1. 使能模拟电源 */
    ret = regulator_enable(priv->avdd_reg);
    if (ret) return ret;
    usleep_range(2000, 3000);  /* 等待2ms */

    /* 2. 使能IO电源 */
    ret = regulator_enable(priv->iovdd_reg);
    if (ret) return ret;
    usleep_range(1000, 2000);

    /* 3. 使能数字核心电源 */
    ret = regulator_enable(priv->dvdd_reg);
    if (ret) return ret;
    usleep_range(1000, 2000);

    /* 4. 使能MCLK */
    ret = clk_prepare_enable(priv->xvclk);
    if (ret) return ret;
    usleep_range(1000, 2000);

    /* 5. 释放复位 */
    gpiod_set_value_cansleep(priv->reset_gpio, 1);
    usleep_range(10000, 15000);  /* 等待10ms以上 */

    dev_dbg(dev, "power on sequence completed\n");
    return 0;
}

static int sensor_power_off(struct sensor_priv *priv)
{
    /* 1. 拉低复位 */
    gpiod_set_value_cansleep(priv->reset_gpio, 0);
    usleep_range(1000, 2000);

    /* 2. 关闭MCLK */
    clk_disable_unprepare(priv->xvclk);

    /* 3. 按逆序关闭电源 */
    regulator_disable(priv->dvdd_reg);
    regulator_disable(priv->iovdd_reg);
    regulator_disable(priv->avdd_reg);

    dev_dbg(&priv->subdev.dev, "power off sequence completed\n");
    return 0;
}
Sensor上下电时序对比图 上电序列 (power_on) AVDD使能 DOVDD使能 DVDD使能 MCLK使能 RST释放 2ms 1ms 1ms 1ms 10ms 下电序列 (power_off) RST拉低 MCLK关闭 DVDD关闭 DOVDD关闭 AVDD关闭 ⚠ 上电与下电必须严格遵循硬件手册指定的时序与延迟 违反时序可能导致Sensor初始化失败或硬件损坏

3.4 s_stream回调函数:数据流的启停控制

s_stream是V4L2子设备操作集v4l2_subdev_video_ops中的核心回调,用于控制Sensor的数据流输出。当应用层调用VIDIOC_STREAMON时,V4L2框架会逐级调用子设备的s_stream(1);调用VIDIOC_STREAMOFF时调用s_stream(0)。

s_stream(1)的典型实现步骤:

  1. 调用sensor_power_on确保设备处于上电状态。
  2. 通过I2C写入Sensor寄存器组,配置分辨率、帧率、曝光、增益等参数。
  3. 写入0x0100(Stream_On)寄存器为0x01,启动MIPI数据输出。
  4. 设置priv->streaming = true标志位。

s_stream(0)的典型实现步骤:

  1. 写入0x0100寄存器为0x00,停止MIPI数据输出。
  2. 调用sensor_power_off下电以降低功耗。
  3. 设置priv->streaming = false标志位。
性能优化提示:在s_stream(1)中,寄存器配置应尽量使用批量写入(I2C多字节写或寄存器数组),减少I2C通信次数。对于帧率切换频繁的场景,可将常用配置预计算并缓存到priv结构体中,避免每次stream on都重新计算。
static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
    struct sensor_priv *priv = v4l2_get_subdevdata(sd);
    int ret = 0;

    if (enable) {
        /* 1. 上电 */
        ret = sensor_power_on(priv);
        if (ret)
            return ret;

        /* 2. 写入初始化寄存器组 */
        ret = sensor_write_array(priv, sensor_init_regs,
                                 ARRAY_SIZE(sensor_init_regs));
        if (ret)
            goto err_power_off;

        /* 3. 写入分辨率配置 */
        ret = sensor_write_array(priv, sensor_resolution_regs,
                                 ARRAY_SIZE(sensor_resolution_regs));
        if (ret)
            goto err_power_off;

        /* 4. 启动MIPI流 */
        ret = sensor_write_reg(priv, 0x0100, 0x01);
        if (ret)
            goto err_power_off;

        priv->streaming = true;
        dev_dbg(sd->dev, "stream started\n");
    } else {
        /* 停止MIPI流 */
        ret = sensor_write_reg(priv, 0x0100, 0x00);
        if (ret)
            return ret;

        /* 下电 */
        sensor_power_off(priv);
        priv->streaming = false;
        dev_dbg(sd->dev, "stream stopped\n");
    }

    return 0;

err_power_off:
    sensor_power_off(priv);
    return ret;
}
s_stream回调函数状态机 STREAM_OFF priv->streaming = false power_on 上电 + 时钟使能 regs配置 写入初始化寄存器组 stream on 0x0100 = 0x01 STREAM_ON priv->streaming = true stream off + power_off 0x0100 = 0x00 → 下电 s_stream(1) s_stream(0) 回到STREAM_OFF

3.5 四个回调函数的协作关系

在RK平台Sensor驱动的完整生命周期中,probe、remove、power_on/off、s_stream四个回调函数按照以下时序协作:

阶段 调用函数 关键动作 典型耗时
驱动加载 probe 设备树解析、资源获取、CHIP_ID校验、V4L2注册 50~200ms
应用打开设备 (无回调,V4L2框架自动处理) 创建文件句柄、分配缓冲区 1~5ms
开始采集 s_stream(1) → power_on 上电、寄存器配置、启动MIPI流 30~100ms
持续采集 (中断/轮询处理帧数据) DMA传输、帧同步、缓冲区入队出队 每帧10~40ms
停止采集 s_stream(0) → power_off 停止MIPI流、下电 10~30