4. 设备树与Camera硬件抽象
在i.MX Linux BSP中,Camera子系统能否被正确枚举、上电、出图,几乎完全取决于设备树(Device Tree)对硬件拓扑的描述是否准确。内核里的CSI/V4L2驱动、I2C控制器驱动、pinctrl子系统、时钟框架和regulator框架本身都是通用代码,它们并不知道板子上接的是OV5640还是IMX219,也不知道复位脚接在哪个GPIO、MCLK由哪路PLL分频而来。设备树的作用,就是把这些“板级事实”翻译成内核可解析的节点与属性,让驱动在probe阶段按图索骥地完成资源申请与硬件初始化。
理解这一节,建议先建立一个整体视角:Camera硬件抽象不是单个节点,而是一张由SoC内部控制器节点、板级连接节点和外部传感器节点共同组成的引用网络。CSI接收器、I2C总线、GPIO控制器、CCM时钟、电源域和传感器分别位于不同的设备树层级,通过phandle(节点引用)和标准属性互相指向。任何一处引用错误,都会在启动日志中以probe defer、-EPROBE_DEFER或直接probe失败的形式暴露出来。
先看CSI节点。以i.MX8M Mini为例,CSI接收器通常写作csi@32e30000或mipi_csi@32e30000,其compatible为"fsl,imx8mm-csi"。真正决定“数据从哪来”的不是CSI节点本身,而是它下面的port/endpoint子节点。端点中的remote-endpoint属性用phandle指向传感器节点的输出端点,从而在V4L2异步子设备框架中建立一条media link。如果这条link没有建立,即使传感器驱动probe成功,/dev/video0也不会出现在应用层。端点里还会描述总线类型和lane数量,例如MIPI CSI-2场景下bus-type = <4>、data-lanes = <1 2 3 4>、clock-lanes = <0>;并口场景则用bus-width、hsync-active、vsync-active、pclk-sample等属性描述时序极性。
I2C节点承担的是控制通道。传感器作为I2C从设备挂在某条I2C总线下面,节点名通常为ov5640: camera@3c,其中@3c就是7位从地址0x3C。父节点I2C控制器需要正确配置clock-frequency,常见取100000或400000。这里有一个容易忽略的点:传感器节点的reg必须与硬件实际地址一致,且不能与同总线上其他器件冲突;如果地址写错,驱动probe时会报-ENXIO或-EREMOTEIO,表现为“找不到设备”。
GPIO节点负责复位和掉电控制。传感器节点中通过reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>和powerdown-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>这样的属性声明引脚归属与有效电平。这里的GPIO_ACTIVE_LOW表示低电平有效,驱动在调用gpiod_set_value()时会自动做逻辑到物理的翻转。很多“传感器识别不到”的问题,根源就是有效电平写反,导致复位时序一直处于复位态。上电时序通常要求:先使能电源,再释放powerdown,最后拉高reset并保持至少1ms,之后才能访问I2C寄存器。
时钟节点通过CCM提供MCLK。传感器节点里写clocks = <&clk IMX8MM_CLK_CSI1_CORE>或指向某个固定时钟源,并用clock-names = "xclk"与驱动中的devm_clk_get(&client->dev, "xclk")对应。若传感器需要24MHz输入,可在板级节点中用assigned-clocks和assigned-clock-rates = <24000000>设定。时钟没使能时,I2C可能仍能读到ID,但出图会失败或帧率异常,因为传感器内部PLL没有参考时钟。
电源抽象由regulator节点完成。传感器节点通过vdd-supply、dovdd-supply、avdd-supply等属性引用对应的regulator。驱动probe时会调用regulator_get()和regulator_enable(),按顺序上电。如果regulator被标记为always-on,则不受驱动控制;若需要动态开关,则要确保regulator驱动本身已就绪,否则会出现-EPROBE_DEFER,传感器驱动会延迟重试,直到电源就绪。
把这些节点串起来看,一次成功的Camera枚举大致经历:I2C控制器就绪 → 传感器节点被创建 → 驱动匹配compatible → 申请GPIO、时钟、regulator → 执行上电与复位时序 → 读取芯片ID确认通信 → 注册V4L2子设备 → CSI端点建立media link → 生成/dev/video*。任何一步资源缺失,都会在dmesg中留下线索。排查时建议按“电源→时钟→复位→I2C通信→CSI link”的顺序逐项确认,而不是一上来就怀疑驱动代码。
ls /proc/device-tree/查看节点是否存在;cat /sys/kernel/debug/clk/clk_summary确认MCLK是否使能;i2cdetect -y 1验证传感器地址是否应答;media-ctl -p查看media link是否建立。最后给出一个简化的设备树片段,帮助建立整体印象。注意其中&i2c1、&csi、&gpio1等标签都来自SoC级dtsi,板级dts只做覆盖和补充,这也是i.MX BSP分层设计的关键:SoC共性放在dtsi,板级差异放在dts。
&i2c1 {
status = "okay";
clock-frequency = <400000>;
ov5640: camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_csi>;
clocks = <&clk IMX8MM_CLK_CSI1_CORE>;
clock-names = "xclk";
assigned-clocks = <&clk IMX8MM_CLK_CSI1_CORE>;
assigned-clock-rates = <24000000>;
reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
powerdown-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
vdd-supply = <®_cam_1v8>;
dovdd-supply = <®_cam_2v8>;
port {
ov5640_out: endpoint {
remote-endpoint = <&csi_in>;
data-lanes = <1 2>;
clock-lanes = <0>;
bus-type = <4>;
};
};
};
};
&csi {
status = "okay";
port {
csi_in: endpoint {
remote-endpoint = <&ov5640_out>;
};
};
};
掌握这张“节点—属性—引用”的对应关系后,再去看BSP里现成的dts文件,就能快速定位每个属性的作用,也能在更换传感器或改板时,知道该动哪几个节点、哪些属性必须同步修改。设备树不是配置脚本,而是硬件描述;描述准确,驱动才能按预期工作。