3、设备树中Camera节点定义:标准属性、时钟电源与GPIO控制

好,咱们进入正题。Camera节点的设备树怎么写?说白了,就是告诉内核三件事:你是谁、怎么找到你、怎么让你干活。这三件事分别对应标准属性、时钟电源属性和GPIO控制属性。我一个个拆开讲。

3.1 标准属性:compatible、reg、interrupts

这三个属性是设备节点的身份证。没有它们,内核根本认不出你的Camera。

3.1.1 compatible:驱动匹配的关键

compatible属性决定了内核用哪个驱动来绑定这个设备。格式是"厂商,型号"。比如:

compatible = "sony,imx219", "ovti,ov5640";

这里我写两个,是给内核一个备选方案。第一个匹配不上,就试第二个。我在项目中遇到过,某款Sensor的驱动只认"ovti,ov5640",但硬件上贴的是另一家的片子,结果驱动死活加载不上。后来发现是compatible写错了。

注意:compatible字符串必须与驱动源码中of_match_table里的字符串完全一致,大小写、连字符都不能错。我曾经因为"imx219"写成了"IMX219",排查了整整一下午。

3.1.2 reg:I2C地址或片选

对于Camera来说,reg通常就是I2C从设备地址。比如:

reg = <0x10>;

这个0x10是7位地址。注意,有些Sensor手册给的是8位地址(比如0x20),你需要右移一位再填进去。嗯,这里容易踩坑。

如果Camera接在SPI总线上,reg就是片选号。比如:

reg = <0>;  // 片选CS0

3.1.3 interrupts:中断引脚

Camera通常用中断来通知SoC有数据准备好了。比如:

interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;

这里GPIO1_5作为中断源,上升沿触发。我个人的习惯是,如果Sensor支持,尽量用边沿触发而不是电平触发,能省不少CPU资源。

小技巧:IRQ_TYPE_EDGE_RISING这类宏定义在dt-bindings/interrupt-controller/irq.h里,记得include进来。

3.2 时钟与电源属性

Camera是耗电大户,时钟和电源必须配好。否则Sensor要么不工作,要么图像有纹波。

3.2.1 时钟属性:clocks与clock-names

Camera需要外部时钟源,通常是24MHz或27MHz的MCLK。设备树里这样写:

clocks = <&clkc 15>;
clock-names = "xvclk";

clocks指向时钟控制器,后面的数字是时钟ID。clock-names是给驱动用的标签,驱动通过名字来获取时钟句柄。

我记得有一次,Sensor输出的图像一直有条纹干扰。查了半天,发现是MCLK的精度不够。后来换了一个高精度的PLL时钟源,问题就解决了。所以时钟质量很重要,别随便找个GPIO模拟时钟就往上怼。

3.2.2 电源属性:AVDD、DVDD、IOVDD

Camera通常需要三路电源:模拟电源AVDD(2.8V)、数字核心电源DVDD(1.2V或1.8V)、IO电源IOVDD(1.8V或3.3V)。设备树里用regulator来定义:

avdd-supply = <®_cam_avdd>;
dvdd-supply = <®_cam_dvdd>;
iovdd-supply = <®_cam_iovdd>;

这些regulator需要在设备树的其他地方定义好电压值。驱动在probe时会按顺序上电,你想想看,如果上电顺序错了,Sensor可能初始化失败。

上电时序要求:大多数Sensor要求先上IOVDD,再上AVDD,最后上DVDD。关电时顺序相反。这个时序在Sensor datasheet里都有,千万别想当然。

3.3 GPIO控制属性

Camera的GPIO控制,说白了就是复位和使能。这两个引脚控制不好,Sensor就跟你耍脾气。

3.3.1 复位引脚:reset-gpios

复位引脚用于将Sensor恢复到初始状态。设备树里这样写:

reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;

GPIO_ACTIVE_LOW表示低电平复位。驱动在初始化时会先拉低复位引脚,保持一段时间,再拉高释放。这个保持时间,不同Sensor要求不一样,有的要1ms,有的要10ms。我建议你看完datasheet再写代码。

3.3.2 使能引脚:powerdown-gpios或enable-gpios

使能引脚控制Sensor是否进入工作模式。有些Sensor叫PWDN(Power Down),有些叫STANDBY。写法类似:

powerdown-gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>;

这里GPIO_ACTIVE_HIGH表示高电平进入掉电模式。驱动在初始化时需要拉低这个引脚,让Sensor退出掉电状态。

我曾经踩过的坑:有一次我把reset-gpios和powerdown-gpios的极性搞反了。结果Sensor上电后一直处于复位状态,I2C通信都建立不起来。后来用示波器一抓,发现复位引脚一直是低电平。嗯,从那以后我写GPIO属性都会再三确认极性。

3.3.3 其他GPIO:闪光灯、聚焦等

有些Camera模组还带有闪光灯控制或自动聚焦功能,也需要GPIO:

flash-gpios = <&gpio4 0 GPIO_ACTIVE_HIGH>;
focus-gpios = <&gpio4 1 GPIO_ACTIVE_HIGH>;

这些不是必须的,但如果你要支持闪光灯或AF,就得加上。

3.4 完整示例:一个典型的Camera节点

说了这么多,咱们看一个完整的例子。以OV5640为例:

&i2c2 {
    status = "okay";
    
    ov5640: camera@3c {
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        
        interrupt-parent = <&gpio1>;
        interrupts = <5 IRQ_TYPE_EDGE_RISING>;
        
        clocks = <&clkc 15>;
        clock-names = "xvclk";
        
        avdd-supply = <®_cam_avdd>;
        dvdd-supply = <®_cam_dvdd>;
        iovdd-supply = <®_cam_iovdd>;
        
        reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
        powerdown-gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>;
        
        port {
            ov5640_ep: endpoint {
                remote-endpoint = <&csi_ep>;
                bus-type = <4>;  // MIPI CSI-2
                data-lanes = <1 2>;
            };
        };
    };
};

这个节点包含了我们刚才讲的所有内容。port部分定义的是数据接口,这个后面章节会详细讲。

我的建议:写设备树时,先把标准属性写好,再配时钟电源,最后加GPIO。按这个顺序来,不容易漏东西。而且每加一个属性,都去datasheet里确认一下,别凭记忆写。

好了,Camera节点的标准属性、时钟电源和GPIO控制就讲到这里。下一节咱们聊聊数据接口——MIPI CSI-2和并行接口在设备树里怎么配。那个更有意思。