资讯详情

资讯详情

msm8953平台Camera移植实战:DTS配置、驱动适配与v4l2出图验证

1. 项目缘起与整体设计思路1.1 为什么选这个平台做Camera移植手里这块基于msm8953的开发板属于高通骁龙625系列八核A53架构14nm制程在当年是中端主力现在拿来做嵌入式视觉项目依然够用。我接到的需求是把一颗MIPI接口的CMOS传感器点亮跑通从设备树配置到用户空间出图的完整链路。这颗sensor不是原厂参考设计里默认的那颗所以DTS要改、驱动要适配、时钟和电源域要重新对最后还得用v4l2-ctl抓一帧确认数据通路没问题。选这个平台的原因很实际msm8953的Camera子系统CAMSS架构成熟资料相对完整社区里踩过坑的人多遇到问题容易找到线索。而且它的ISP管线支持RAW到YUV的处理对于不想在外挂ISP上多花钱的方案来说直接走平台内置ISP是最省事的路径。整个移植工作的核心目标就一个让/dev/video0节点出现并且通过v4l2-ctl能抓到一帧正常的图像。听起来简单但中间涉及DTS的CSI、CCI、 regulator、clock、GPIO多个节点的联动任何一个对不上sensor就probe失败或者出图花屏。1.2 整体方案拆解与选型考量移植路线我分成了四层硬件层确认供电和时钟、DTS层描述设备连接关系、驱动层适配sensor寄存器序列、用户层验证数据通路。这四层是递进关系前面不对后面白搭。硬件层要先确认三件事sensor的供电轨通常有AVDD、DVDD、DOVDD三路、MCLK时钟频率常见24MHz或19.2MHz、以及I2C地址7位地址常见0x10、0x36、0x6c等。这些信息从sensor的datasheet里拿拿不到就找模组厂要初始化序列。DTS层是移植的重头戏。msm8953的Camera DTS结构比较绕CSIMIPI接收控制器、CCII2C控制器、CSIPHY物理层、CSID解码层是分开描述的sensor节点要挂到CCI下面同时通过remote-endpoint和CSIPHY建立连接。这个连接关系如果画错了media controller里就看不到完整的pipeline。驱动层我选择基于平台已有的sensor驱动框架改而不是从零写。msm8953的kernel里通常有ov5648、ov8865这类参考驱动把寄存器序列、分辨率配置、时钟配置替换成目标sensor的就行。这样能省掉大量V4L2子设备注册的样板代码。用户层验证用v4l2-ctl这是最直接的工具。先media-ctl -p看pipeline拓扑确认sensor、csiphy、csid、isp这几个entity都连上了再用v4l2-ctl --stream-mmap抓帧存成raw文件最后用工具转成能看到图片。注意msm8953的Camera子系统在不同kernel版本上差异较大3.18和4.9的DTS绑定方式就不一样。动手前先确认你手上的BSP是哪个版本别拿错参考文档。2. DTS配置的核心细节与实操要点2.1 CCI与Sensor节点的挂载关系CCI在msm8953里就是I2C控制器但它在DTS里的描述方式和普通I2C不太一样。你要在cci节点下面建一个i2c的subnode然后把sensor挂进去。关键属性是regI2C从地址和compatible匹配驱动。cci { status ok; i2c1 { reg 1; #address-cells 1; #size-cells 0; camera_sensor: camera-sensor36 { compatible vendor,sensor-name; reg 0x36; status ok; /* 供电配置 */ vdd-supply pm8953_l10; vddio-supply pm8953_l6; vdda-supply pm8953_l3; /* 时钟配置 */ clocks clock_gcc clk_mclk0_clk_src; clock-names cam_src_clk; clock-frequency 24000000; /* GPIO配置 */ reset-gpios tlmm 26 0; pwdn-gpios tlmm 36 0; /* CSI连接 */ port { sensor_out: endpoint { remote-endpoint csiphy0_ep; >csiphy0 { status ok; csi-vdd-supply pm8953_l2; port { csiphy0_ep: endpoint { remote-endpoint sensor_out; >cci { assigned-clocks clock_gcc clk_mclk0_clk_src; assigned-clock-rates 24000000; };电源域这边msm8953的Camera供电通常走PM8953的LDO。L10给AVDD2.8VL6给DOVDD1.8VL3给DVDD1.2V。具体哪路LDO给哪个电压得看原理图。我遇到过LDO电压设错导致sensor I2C能通但不出图的情况查了半天才发现是DVDD只有1.0Vsensor内部PLL没锁上。实操心得DTS改完之后先别急着编译整个kernel用dtc工具单独编译dtb然后反编译回来检查节点关系是否正确。命令是dtc -I dtb -O dts -o check.dts arch/arm64/boot/dts/qcom/msm8953.dtb这样能快速发现语法错误。3. 驱动适配与寄存器序列调试3.1 基于参考驱动的移植策略msm8953的kernel里通常自带几个sensor驱动比如ov5648、ov8865、imx214。我选了一个寄存器结构最接近的作为模板把里面的sensor ID、分辨率表、寄存器序列全部替换掉。驱动文件一般放在drivers/media/platform/msm/camera_v2/sensor/下面。核心结构体是msm_sensor_ctrl_t里面包含了sensor_lib_params、sensor_output、sensor_reg_tbl这些关键字段。移植时重点改这几个地方sensor_lib_params里的full_size、prev_size、video_size等分辨率定义sensor_output里的width、height、line_length_pclk、frame_length_linessensor_reg_tbl里的初始化寄存器序列msm_sensor_power_setting里的上电时序上电时序特别关键。一般顺序是先给AVDD延时1ms再给DOVDD延时1ms再给DVDD然后拉高reset延时1ms拉低reset最后给MCLK。这个顺序如果错了sensor可能直接锁死I2C都读不到ID。3.2 寄存器序列的调试方法寄存器序列通常从sensor厂拿但拿到的往往是针对特定模组的换一个镜头或者换一个供电方案就可能需要微调。调试的时候我习惯把序列分成几段PLL配置段、模拟配置段、时序配置段、输出格式段。PLL配置决定了sensor内部的工作时钟如果PLL没锁上I2C能读到ID但出不了图。验证方法是读PLL相关的状态寄存器看lock位是否置起。模拟配置段包括增益、曝光、黑电平这些一般用默认值就行出图后再调。时序配置段是最容易出问题的包括HTS、VTS、HBlank、VBlank这些。如果HTS设小了一行数据没传完就被截断表现为图像右边有绿边或者错位。VTS设小了帧同步会乱表现为图像滚动或者撕裂。输出格式段决定sensor输出的是RAW8、RAW10还是RAW12以及是MIPI还是DVP。msm8953的CSID支持RAW8/10/12但ISP处理RAW10最顺所以一般配成RAW10。/* 典型的RAW10输出配置 */ static struct msm_sensor_output_info_t sensor_output_info { .x_output 1920, .y_output 1080, .line_length_pclk 2200, .frame_length_lines 1125, .vt_pixel_clk 148500000, .op_pixel_clk 148500000, .binning_factor 1, .max_fps 30, };line_length_pclk和frame_length_lines这两个值要算对。以1920x108030fps为例MIPI时钟如果是594Mbps每lane四lane总带宽是2376MbpsRAW10每像素10bit所以理论最大像素率是237.6M像素每秒。1920x1080是207万像素30fps需要62.2M像素每秒带宽是够的。HTS和VTS的乘积要大于等于总像素数除以帧率。3.3 驱动probe失败的排查思路sensor驱动probe失败是最常见的问题表现是/dev/video0不出现dmesg里有probe failed或者i2c read failed。排查顺序我一般这样走先确认I2C通不通。用i2cdetect -y 1扫一下看sensor地址能不能被识别。如果扫不到查供电和上拉电阻。I2C通了但probe失败查sensor ID。驱动里一般会读sensor的ID寄存器和预期值比对。读到的ID不对可能是I2C地址错了或者寄存器地址位宽不对8位还是16位。ID对了但probe还是失败查时钟。用示波器量MCLK看频率对不对有没有波形。没有波形就查时钟树配置。时钟对了但probe失败查reset和pwdn GPIO。这两个GPIO的极性很重要有的sensor是高电平复位有的是低电平。搞反了sensor一直处于复位状态。注意msm8953的CCI控制器在probe阶段可能会因为时钟没准备好而超时dmesg里会打印cci timeout。这时候不要急着改驱动先确认CCI的时钟源有没有使能。4. v4l2-ctl出图验证与问题排查4.1 media controller拓扑检查出图之前先用media-ctl看pipeline拓扑确认所有entity都连上了。media-ctl -p -d /dev/media0正常输出里应该能看到sensor、csiphy、csid、ispif、isp这几个entity并且它们之间的link是ENABLED状态。如果某个link是IMMUTABLE或者DISABLED说明DTS里的endpoint没配对。我遇到过csiphy和csid之间的link没使能的情况原因是csid的endpoint名字写错了。DTS里csid的endpoint必须叫csid0_ep写成csid_ep就匹配不上。拓扑确认之后用media-ctl设置格式media-ctl -d /dev/media0 --set-v4l2 camera-sensor:0[fmt:SRGGB10_1X10/1920x1080] media-ctl -d /dev/media0 --set-v4l2 msm_csiphy0:0[fmt:SRGGB10_1X10/1920x1080] media-ctl -d /dev/media0 --set-v4l2 msm_csid0:0[fmt:SRGGB10_1X10/1920x1080]格式要一层层往下设从sensor开始到csiphy到csid最后到ispif。如果中间某一层设不进去说明那一层的驱动没准备好。4.2 v4l2-ctl抓帧命令与参数抓帧用v4l2-ctl的stream-mmap模式v4l2-ctl -d /dev/video0 --set-fmt-videowidth1920,height1080,pixelformatRG10 --stream-mmap --stream-count1 --stream-toframe.rawpixelformat要和你sensor输出的格式对应。RAW10对应RG10Bayer RGGBRAW8对应RGGB。写错了会报VIDIOC_S_FMT failed。抓到的raw文件是纯Bayer数据不能直接看要用工具转。我一般用raw2rgb或者自己写个python脚本import numpy as np width, height 1920, 1080 with open(frame.raw, rb) as f: data f.read() # RAW10打包格式每5字节存4个像素 pixels np.zeros((height, width), dtypenp.uint16) idx 0 for i in range(0, len(data), 5): b0, b1, b2, b3, b4 data[i:i5] pixels.flat[idx] ((b0 2) | (b4 0x03)) pixels.flat[idx1] ((b1 2) | ((b4 2) 0x03)) pixels.flat[idx2] ((b2 2) | ((b4 4) 0x03)) pixels.flat[idx3] ((b3 2) | ((b4 6) 0x03)) idx 4 # 归一化到8bit pixels (pixels 2).astype(np.uint8) # 简单的Bayer转RGB rgb np.stack([pixels, pixels, pixels], axis-1) from PIL import Image Image.fromarray(rgb).save(frame.png)这个脚本把RAW10解包成16bit再右移2位变8bit最后存成灰度图。虽然颜色不对因为没做Bayer插值但能看出图像结构对不对。4.3 常见出图问题速查表现象可能原因排查方法全黑sensor没出数据查MCLK、reset、I2C是否正常全绿数据线序错检查data-lanes配置尝试调整lane顺序横条纹MIPI信号质量差查CSIPHY供电降低MIPI速率试试图像错位HTS/VTS配置错重新计算line_length_pclk和frame_length_lines滚动帧同步丢失查VTS是否小于实际帧长花屏数据格式不匹配确认sensor输出格式和CSID配置一致只有一半图像lane数不对检查data-lanes属性确认实际用了几个lane实操心得出图有问题时先把分辨率降到最低比如640x480把MIPI速率也降到最低这样能排除带宽和信号完整性问题。低分辨率能出图再逐步往上加定位问题更快。5. 移植过程中的经验沉淀与扩展思考5.1 踩过的坑与避坑指南第一个坑是DTS的endpoint名字。msm8953的DTS里CSIPHY的endpoint名字在不同BSP里不一样有的叫csiphy0_ep有的叫csiphy_ep。名字对不上media controller里就看不到link。我的做法是先把BSP里自带的参考DTS翻出来照着它的命名规则写别自己发明。第二个坑是时钟频率。sensor datasheet上写的MCLK是24MHz但平台默认给的是19.2MHz。我一开始没改时钟树直接改sensor的PLL配置结果PLL锁不上。后来把assigned-clock-rates改成24000000问题解决。所以遇到PLL锁不上先确认MCLK频率对不对。第三个坑是电源上电顺序。我按照datasheet的顺序写但实际模组上有一颗LDO的使能脚接在reset GPIO上导致reset拉高的时候LDO才输出。这种情况下要把reset GPIO的配置改成先拉高再配置LDO或者干脆把LDO的使能脚改到别的GPIO上。第四个坑是v4l2-ctl的pixelformat。RAW10在V4L2里的fourcc是RG10但有些kernel版本里是BA10或者RGGB。写错了会报错但错误信息很模糊只说S_FMT failed不告诉你哪个格式不对。我的做法是把所有可能的fourcc都试一遍用v4l2-ctl --list-formats看驱动支持哪些。5.2 性能调优与稳定性验证出图只是第一步后面还要调帧率、调曝光、调白平衡。帧率上不去通常是VTS设太大了把VTS减小能提高帧率但太小了会丢帧。我一般把VTS设成实际帧长的1.1倍留一点余量。曝光和白平衡这些3A算法msm8953的ISP里有硬件支持但需要用户空间配合。如果只是验证数据通路可以先用固定曝光和固定增益出图正常了再调3A。稳定性验证我一般跑连续抓帧抓1000帧看有没有丢帧或者花屏。命令是v4l2-ctl -d /dev/video0 --stream-mmap --stream-count1000 --stream-to/dev/null如果中间报错dmesg里会有CSID的error log根据log定位是MIPI错误还是ISP错误。5.3 后续可扩展的方向这套移植流程跑通之后可以往几个方向扩展。一是加第二颗sensor做双摄同步这需要配置CSIPHY的lane分配和CSID的虚拟通道。二是接ISP的3A算法把图像质量调上去。三是把v4l2的接口封装成GStreamer插件方便上层应用调用。双摄同步的难点在于两颗sensor要共享MCLK并且帧同步信号要连在一起。msm8953支持MCLK0和MCLK1两路输出但帧同步需要额外的GPIO。这个我在另一个项目里做过后面可以单独写一篇。ISP 3A的调优是个大工程涉及AE、AWB、AF三个模块的参数整定。msm8953的ISP支持硬件统计用户空间只需要读统计值然后算控制量。这部分高通有参考代码但文档很少基本靠读代码和试参数。GStreamer封装相对简单写一个v4l2src的element就行但要注意buffer的零拷贝不然CPU占用会很高。msm8953的ISP支持直接输出到ION内存GStreamer这边用dmabuf导入就能实现零拷贝。最后再分享一个小技巧调试Camera的时候dmesg的log级别调到7能看到CSID和ISP的详细寄存器读写记录。命令是echo 7 /proc/sys/kernel/printk这样排查问题会快很多。不过log量很大建议用串口抓别用adb不然会丢log。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →