4. Sensor驱动初始化流程

Sensor驱动的初始化流程是V4L2 Subdev开发的核心环节,它决定了设备能否被内核正确识别、电源能否稳定供给、以及上层应用能否通过Video节点访问图像数据。本章将深入剖析probe函数的完整编写范式,涵盖设备树解析、电源管理(regulator与clock)以及v4l2_subdev注册等关键步骤。

核心要点: Sensor驱动的probe函数是设备生命周期的起点,其执行顺序直接影响后续所有操作。一个健壮的probe流程应遵循“先解析、后使能、再注册”的原则。

4.1 probe函数整体架构

probe函数是驱动与内核设备模型交互的入口。当设备树中描述的Sensor节点与驱动匹配时,内核调用probe函数。其典型执行流程如下:

  1. 设备树解析: 从设备节点中读取regulator、clock、GPIO、数据接口等配置信息。
  2. 电源管理初始化: 获取并使能regulator和clock资源,确保Sensor处于工作状态。
  3. 硬件初始化: 通过I2C/SPI总线写入寄存器序列,完成Sensor内部配置。
  4. V4L2 Subdev注册: 初始化并注册v4l2_subdev结构体,将其挂载到V4L2框架中。
  5. 媒体实体与链接: 创建media_entity并定义pad之间的数据流链接。
开发建议: 在probe函数中,每一步操作都应检查返回值,并使用dev_err()打印错误信息。推荐使用devm_系列API(如devm_regulator_get)简化资源管理。
Sensor驱动probe函数执行流程图 1. 设备树解析 (of_match) 2. 电源管理初始化 (regulator/clock) 3. 硬件初始化 (寄存器写入) 4. v4l2_subdev 注册 5. 媒体实体与链接创建 6. probe 成功返回 0 错误返回 -ENODEV 错误返回 -EIO

4.2 设备树解析

设备树(Device Tree)是描述硬件拓扑和资源配置的标准方式。Sensor驱动通过of_match_table匹配设备节点,并使用OF API解析关键属性。

4.2.1 设备树节点示例

// imx219_sensor.dtsi 片段
i2c@ff160000 {
    #address-cells = <1>;
    #size-cells = <0>;
    reg = <0xff160000 0x1000>;

    sensor_imx219: camera-sensor@10 {
        compatible = "sony,imx219";
        reg = <0x10>;
        clocks = <&clk_cam 0>;
        clock-names = "xvclk";
        avdd-supply = <&reg_cam_avdd>;
        dvdd-supply = <&reg_cam_dvdd>;
        dovdd-supply = <&reg_cam_dovdd>;
        reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
        port {
            sensor_ep: endpoint {
                remote-endpoint = <&csi_ep>;
                data-lanes = <1 2>;
                link-frequencies = /bits/ 64 <456000000>;
            };
        };
    };
};

4.2.2 驱动中解析设备树的典型代码

static int sensor_parse_dt(struct device *dev, struct sensor_state *sensor)
{
    struct device_node *node = dev->of_node;
    struct device_node *ep_node;
    int ret;

    /* 解析时钟 */
    sensor->xvclk = devm_clk_get(dev, "xvclk");
    if (IS_ERR(sensor->xvclk)) {
        dev_err(dev, "failed to get xvclk\n");
        return PTR_ERR(sensor->xvclk);
    }

    /* 解析电源 regulator */
    sensor->avdd = devm_regulator_get(dev, "avdd");
    sensor->dvdd = devm_regulator_get(dev, "dvdd");
    sensor->dovdd = devm_regulator_get(dev, "dovdd");
    if (IS_ERR(sensor->avdd) || IS_ERR(sensor->dvdd) || IS_ERR(sensor->dovdd)) {
        dev_err(dev, "failed to get regulators\n");
        return -ENODEV;
    }

    /* 解析 GPIO 复位引脚 */
    sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
    if (IS_ERR(sensor->reset_gpio)) {
        dev_err(dev, "failed to get reset gpio\n");
        return PTR_ERR(sensor->reset_gpio);
    }

    /* 解析 endpoint 属性(数据通道、链路频率) */
    ep_node = of_graph_get_next_endpoint(node, NULL);
    if (!ep_node) {
        dev_err(dev, "no endpoint found\n");
        return -EINVAL;
    }
    ret = of_property_read_u32_array(ep_node, "data-lanes",
                                     sensor->data_lanes, 2);
    if (ret < 0) {
        dev_err(dev, "failed to read data-lanes\n");
        of_node_put(ep_node);
        return ret;
    }
    of_node_put(ep_node);

    return 0;
}
常见陷阱: 设备树中的reg地址必须与I2C从设备地址一致;clock-names和supply-names必须与驱动中devm_clk_get和devm_regulator_get的第二个参数完全匹配,否则解析会失败。

4.3 电源管理:regulator与clock

Sensor对电源时序和时钟频率有严格要求。错误的电源管理可能导致图像异常甚至硬件损坏。

4.3.1 Regulator控制

Regulator用于控制Sensor的模拟电源(AVDD)、数字电源(DVDD)和I/O电源(DOVDD)。典型电压值如下表:

电源名称 典型电压 用途 上电顺序
AVDD 2.8V 模拟电路供电 第1步
DVDD 1.2V 数字核心供电 第2步
DOVDD 1.8V I/O接口供电 第3步

4.3.2 Clock控制

Sensor需要外部主时钟(xvclk)驱动内部PLL。驱动中应设置正确的时钟频率并确保时钟稳定后再进行寄存器配置。

static int sensor_power_on(struct sensor_state *sensor)
{
    int ret;

    /* 1. 使能 AVDD */
    ret = regulator_enable(sensor->avdd);
    if (ret < 0)
        return ret;
    usleep_range(1000, 2000);  /* 等待电压稳定 */

    /* 2. 使能 DVDD */
    ret = regulator_enable(sensor->dvdd);
    if (ret < 0)
        goto err_dvdd;
    usleep_range(1000, 2000);

    /* 3. 使能 DOVDD */
    ret = regulator_enable(sensor->dovdd);
    if (ret < 0)
        goto err_dovdd;
    usleep_range(1000, 2000);

    /* 4. 设置并使能时钟 */
    ret = clk_set_rate(sensor->xvclk, 24000000);  /* 24MHz */
    if (ret < 0)
        goto err_clk;
    ret = clk_prepare_enable(sensor->xvclk);
    if (ret < 0)
        goto err_clk;
    usleep_range(5000, 10000);  /* 等待时钟稳定 */

    /* 5. 释放复位(低电平有效) */
    gpiod_set_value_cansleep(sensor->reset_gpio, 0);
    usleep_range(10000, 20000);

    return 0;

err_clk:
    regulator_disable(sensor->dovdd);
err_dovdd:
    regulator_disable(sensor->dvdd);
err_dvdd:
    regulator_disable(sensor->avdd);
    return ret;
}
Sensor上电时序与电源管理流程 t0 t5 AVDD (2.8V) DVDD (1.2V) DOVDD (1.8V) XCLK (24MHz) RESET# 电源时序关键规则 1. AVDD 必须先于 DVDD 和 DOVDD 稳定。 2. 所有电源稳定后,再使能时钟 XCLK。 3. 时钟稳定后,释放 RESET 引脚(低电平有效)。 4. 每一步之间建议插入 1~10ms 延时,确保电压/时钟稳定。

4.4 v4l2_subdev注册

完成硬件初始化后,驱动需要将Sensor注册为V4L2子设备,以便上层框架能够调用其操作函数。

4.4.1 v4l2_subdev结构体初始化

static const struct v4l2_subdev_ops sensor_subdev_ops = {
    .core   = &sensor_core_ops,
    .video  = &sensor_video_ops,
    .pad    = &sensor_pad_ops,
};

static const struct v4l2_subdev_internal_ops sensor_internal_ops = {
    .registered = sensor_registered,
    .unregistered = sensor_unregistered,
};

static int sensor_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
{
    struct sensor_state *sensor;
    struct v4l2_subdev *sd;
    int ret;

    sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
    if (!sensor)
        return -ENOMEM;

    /* 设备树解析 */
    ret = sensor_parse_dt(&client->dev, sensor);
    if (ret < 0)
        return ret;

    /* 电源上电 */
    ret = sensor_power_on(sensor);
    if (ret < 0)
        return ret;

    /* 硬件初始化(写入寄存器序列) */
    ret = sensor_init_regs(sensor);
    if (ret < 0)
        goto err_power_off;

    /* 初始化 v4l2_subdev */
    sd = &sensor->sd;
    v4l2_i2c_subdev_init(sd, client, &sensor_subdev_ops);
    sd->internal_ops = &sensor_internal_ops;
    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;

    /* 初始化 pad */
    sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
    ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
    if (ret < 0)
        goto err_power_off;

    /* 注册 subdev */
    ret = v4l2_async_register_subdev(sd);
    if (ret < 0) {
        dev_err(&client->dev, "failed to register subdev\n");
        goto err_media_cleanup;
    }

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

err_media_cleanup:
    media_entity_cleanup(&sd->entity);
err_power_off:
    sensor_power_off(sensor);
    return ret;
}

4.4.2 关键API说明

API函数 作用 注意事项
v4l2_i2c_subdev_init() 初始化v4l2_subdev并关联I2C客户端 必须在注册前调用
media_entity_pads_init() 初始化媒体实体的pad pad数量必须与实际硬件一致
v4l2_async_register_subdev() 异步注册subdev到V4L2框架 支持设备热插拔和异步绑定
v4l2_device_register_subdev() 同步注册subdev(传统方式) 需要先有v4l2_device实例
最佳实践: 推荐使用v4l2_async_register_subdev()进行异步注册,这样Sensor驱动可以与CSI接收器驱动解耦,提高系统灵活性。在registered回调中完成媒体链接的创建。

4.5 完整probe函数模板

以下是一个整合了上述所有步骤的probe函数模板,适用于大多数I2C接口的Sensor驱动:

static int sensor_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
{
    struct sensor_state *sensor;
    struct v4l2_subdev *sd;
    int ret;

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

    /* 2. 解析设备树 */
    ret = sensor_parse_dt(&client->dev, sensor);
    if (ret < 0)
        return ret;

    /* 3. 电源上电 */
    ret = sensor_power_on(sensor);
    if (ret < 0)
        return ret;

    /* 4. 读取芯片ID,验证硬件连接 */
    ret = sensor_check_id(sensor);
    if (ret < 0) {
        dev_err(&client->dev, "chip ID mismatch\n");
        goto err_power_off;
    }

    /* 5. 初始化寄存器序列 */
    ret = sensor_init_regs(sensor);
    if (ret < 0)
        goto err_power_off;

    /* 6. 初始化V4L2 subdev */
    sd = &sensor->sd;
    v4l2_i2c_subdev_init(sd, client, &sensor_subdev_ops);
    sd->internal_ops = &sensor_internal_ops;
    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;

    /* 7. 初始化pad */
    sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
    ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
    if (ret < 0)
        goto err_power_off;

    /* 8. 异步注册subdev */
    ret = v4l2_async_register_subdev(sd);
    if (ret < 0) {
        dev_err(&client->dev, "failed to register subdev\n");
        goto err_media_cleanup;
    }

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

err_media_cleanup:
    media_entity_cleanup(&sd->entity);
err_power_off:
    sensor_power_off(sensor);
    return ret;
}
关键检查点: 在probe函数中,务必在注册subdev之前完成所有硬件初始化操作。一旦subdev注册成功,其他内核组件可能会立即调用subdev的操作函数,此时必须确保硬件已处于可用状态。

4.6 本章小结

Sensor驱动的probe函数是驱动开发的基石。本章详细介绍了设备树解析、电源管理(regulator与clock)以及v4l2_subdev注册的完整流程。掌握这些内容后,开发者可以构建出健壮、可移植的Sensor驱动,为后续的ISP协同工作打下坚实基础。