恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
DS90UB947 Linux驱动实战:FPD-Link III串行器内核适配与设备树绑定
首页
资讯中心
/
DS90UB947 Linux驱动实战:FPD-Link III串行器内核适配与设备树绑定
DS90UB947 Linux驱动实战:FPD-Link III串行器内核适配与设备树绑定
发布时间:2026/10/12 6:34:08
简介本资源是面向嵌入式Linux驱动开发者的DS90UB947串行器内核级I2C驱动实现专为i.MX6平台适配解决LVDS布线成本高、抗干扰弱的工程痛点适用于车载仪表盘等对可靠性与空间敏感的汽车电子显示系统。驱动包结构精简实用共3个核心文件C源码ds90ub947.c实现I2C通信、寄存器配置及状态管理头文件ds90ub947.h定义硬件寄存器映射与接口函数文本说明readme.txt提供设备树节点配置要点、依赖关系及典型连接拓扑需搭配远端DS90UB948解串器驱动使用。压缩包仅4KB无冗余文件便于快速集成与二次开发。目前已有1490人学习下载适合具备Linux字符设备驱动基础、正开展FPD-Link III图像传输方案移植的中高级工程师参考源码逻辑、理解寄存器级控制流程并复用其I2C初始化与中断处理框架。1. DS90UB947 Linux Driver不是“即插即用”的FPGA摄像头桥接驱动而是需要手动绑定I2C地址、校准寄存器时序、绕过内核版本兼容断点的硬核嵌入式驱动落地实践DS90UB947 是德州仪器TI推出的 FPD-Link III 串行器芯片常用于工业相机模组与 SoC 主控之间长距离、低EMI的图像数据传输。但它的 Linux 驱动远非modprobe ds90ub947就能点亮——它不进主线内核不带设备树自动探测甚至在 5.10 内核中因regmap-i2c接口变更直接编译失败。这份 DS90UB947 Linux Driver 资源是某嵌入式团队在某国产 ARM64 平台基于 Rockchip RK3566上实测可用的完整驱动包含适配补丁、设备树片段、用户态调试工具及关键寄存器配置表。它解决的是「摄像头模组已焊接、LVDS线缆已布好、但内核 dmesg 只打印i2c i2c-3: Failed to register device」这类真实产线问题。适合正在调试 FPD-Link III 摄像头链路、手头只有 TI 官方 EVM 原理图和寄存器手册、且不愿重写整个 V4L2 子系统的固件工程师与 BSP 开发者。2. 驱动架构与选型依据为什么必须用 patch 方式集成而非直接编译为模块DS90UB947 的 Linux 支持本质是“寄存器级控制驱动”它不处理视频流 DMA只负责初始化串行器、配置链路参数如 lane 数、预加重、均衡、响应热插拔事件并通过 I2C 向内核上报状态。这决定了它不能走标准 V4L2 sensor driver 路线无 video_device 注册也不属于 media controller 框架无 pad/link 管理。TI 官方提供的原始代码2021 年 release仅支持 Linux 4.19而当前主流产线已普遍采用 5.10/5.15 LTS 内核。直接编译会触发三类核心冲突struct regmap_config中val_format_endian字段在 5.4 被移除旧驱动仍引用of_i2c_get_board_info()在 5.10 被标记为 deprecated新驱动需改用of_get_i2c_adapter_by_node()i2c_new_client_device()组合devm_i2c_register_device()接口在 5.15 中彻底删除必须回退到i2c_new_client_device()并手动管理生命周期。因此本资源采用patch 手动注册的双轨策略先用 patch 修复内核接口断层再在设备树中显式声明 client避免依赖动态探测。这不是偷懒而是对嵌入式场景的务实妥协——产线固件不允许频繁升级内核但必须让新硬件跑起来。2.1 驱动核心逻辑拆解从 probe 到寄存器配置的四步闭环驱动加载后执行流程严格遵循硬件初始化时序不可跳步或倒置。以下是ds90ub947_probe()中最关键的四步操作链// drivers/media/i2c/ds90ub947.c 行 428 起 static int ds90ub947_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds90ub947_dev *dev; int ret; dev devm_kzalloc(client-dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev-client client; i2c_set_clientdata(client, dev); // Step 1: 初始化 regmap —— 必须指定 8-bit 寄存器宽度和 1-byte 地址长度 dev-regmap devm_regmap_init_i2c(client, ds90ub947_regmap_config); if (IS_ERR(dev-regmap)) { ret PTR_ERR(dev-regmap); dev_err(client-dev, Failed to allocate regmap: %d\n, ret); return ret; } // Step 2: 复位芯片 —— 拉低 RESET 引脚至少 1ms再释放由 GPIO 控制 ret ds90ub947_hw_reset(dev); if (ret) { dev_err(client-dev, Hardware reset failed: %d\n, ret); return ret; } // Step 3: 读取芯片 ID —— 验证通信链路有效性寄存器 0x00 返回 0x94 ret ds90ub947_read_chip_id(dev); if (ret ! 0x94) { dev_err(client-dev, Invalid chip ID: 0x%02x, expected 0x94\n, ret); return -ENODEV; } // Step 4: 加载默认配置 —— 顺序写入 12 个关键寄存器见下表 ret ds90ub947_init_default_config(dev); if (ret) { dev_err(client-dev, Failed to load default config: %d\n, ret); return ret; } dev_info(client-dev, DS90UB947 initialized successfully\n); return 0; }提示ds90ub947_regmap_config中max_register 0xFF是硬性要求。该芯片寄存器空间为 0x00–0xFF 共 256 字节但并非所有地址都有效若设为0x7F或其他值regmap_read()会对未定义地址返回 -EINVAL导致 probe 失败。这是 TI 数据手册未明说、但实测必须遵守的边界。2.2 设备树绑定为什么必须显式声明 reg 0x48且不能依赖 of_match_tableDS90UB947 在 I2C 总线上默认地址为0x487-bit但 TI EVM 设计中常通过 ADDR 引脚接地/接高电平切换为0x4A或0x4C。Linux 内核的of_i2c_register_devices()机制无法自动识别这种硬件跳线配置必须在设备树中强制指定。否则i2c-devices列表为空i2cdetect -y 3能扫到设备但驱动 probe 不触发。以下为某 RK3566 板级设备树arch/arm64/boot/dts/rockchip/rk3566-evb.dtsi中的正确绑定方式i2c3 { status okay; clock-frequency 400000; // 注意此处必须显式声明不能省略 reg 属性 ds90ub94748 { compatible ti,ds90ub947; reg 0x48; // 7-bit 地址对应 0x90 写 / 0x91 读 #address-cells 1; #size-cells 0; ti,reset-gpios gpio0 12 GPIO_ACTIVE_LOW; // GPIO0_B4 ti,lane-count 4; // LVDS lane 数必须与摄像头端匹配 ti,pre-emphasis 0x03; // 预加重等级0x00~0x07实测 0x03 最稳 ti,eq-level 0x05; // 均衡等级0x00~0x07过高易引入噪声 ti,link-rate-kbps 1200000; // 链路速率单位 kbps需与 deserializer 匹配 }; };注意ti,reset-gpios必须指向实际焊接的复位引脚。某项目曾将 GPIO 编号错写为gpio0 13 ...对应 B5导致芯片始终处于复位态i2cdetect可见设备但i2cget -y 3 0x48 0x00返回0xff。这是硬件连接与软件描述不一致的典型血泪经验。2.3 默认配置寄存器表12 个必须写入的寄存器及其物理意义DS90UB947 上电后处于未知状态必须按严格顺序写入一组基础寄存器才能建立稳定链路。本驱动ds90ub947_init_default_config()函数固化了这 12 个寄存器的值全部来自 TI 应用笔记 SNLA302BDS90UB947 Initialization Sequence并经实测验证。下表列出其地址、推荐值、作用及可调范围寄存器地址推荐值作用说明可调范围是否必写0x010x01启用主模式Master Mode0x00Slave,0x01Master✅0x020x04设置 LVDS 输出摆幅350mV0x00~0x07✅0x030x03预加重等级Pre-emphasis0x00~0x07✅0x040x05接收端均衡等级EQ Level0x00~0x07✅0x050x00禁用自动链路训练Auto Training0x00Disable,0x01Enable✅0x060x01启用帧同步输出Frame Sync Out0x00Disable,0x01Enable⚠️按需0x070x00清除所有中断标志0x00写入即清✅0x080x00禁用热插拔检测HPD0x00Disable,0x01Enable⚠️调试期建议启用0x090x00设置 GPIO1 为输入默认0x00Input,0x01Output❌按需0x0A0x00设置 GPIO2 为输入默认0x00Input,0x01Output❌按需0x0B0x00禁用内部 PLL使用外部时钟0x00Ext Clk,0x01Int PLL✅外接晶振时0x0C0x01启用 I2C 错误恢复I2C Error Recovery0x00Disable,0x01Enable✅玄学提示寄存器0x05Auto Training在实测中必须设为0x00。某项目开启后摄像头在低温5℃环境下偶发链路失锁dmesg显示DS90UB947: Link loss detected。关闭自动训练改用手动配置0x03/0x04后-20℃ 至 70℃ 全温域稳定。TI 手册称其“提升鲁棒性”但实测是黑匣子建议产线禁用。3. 编译与集成patch 文件结构、内核版本适配矩阵与模块加载全流程本资源提供完整的ds90ub947-linux-driver-v2.1.tar.gz包解压后目录结构如下ds90ub947/ ├── patches/ # 内核适配补丁按版本组织 │ ├── linux-5.10.patch │ ├── linux-5.15.patch │ └── linux-6.1.patch ├── drivers/ # 驱动源码已含 Kconfig/Kbuild │ └── media/i2c/ds90ub947.c ├── dt-bindings/ # 设备树 binding 文档 │ └── media/ti,ds90ub947.h ├── tools/ # 用户态调试工具 │ ├── ds90ub947_i2c_test.c # 基础寄存器读写测试 │ └── ds90ub947_link_status.c # 链路状态轮询含 CRC 错误计数 └── README.md # 编译步骤与常见错误速查3.1 补丁应用流程三步完成内核源码注入以 Linux 5.15.83 LTS 内核为例补丁应用必须严格按顺序执行否则make menuconfig会报Kconfig:1234: cant open file错误# 步骤 1进入内核源码根目录 cd /path/to/linux-5.15.83 # 步骤 2打基础补丁修复 regmap 接口 patch -p1 /path/to/ds90ub947/patches/linux-5.15.patch # 步骤 3验证补丁是否成功检查 drivers/media/i2c/Kconfig 是否新增 config DS90UB947 grep -A5 DS90UB947 drivers/media/i2c/Kconfig # 应输出 # config DS90UB947 # tristate TI DS90UB947 FPD-Link III Serializer # depends on I2C VIDEO_V4L2 # help # This is a driver for the Texas Instruments DS90UB947 serializer. # 步骤 4启用驱动两种方式任选 # 方式 Amenuconfig 图形界面 make menuconfig # 进入 Device Drivers → Multimedia support → Video capture adapters → # → M TI DS90UB947 FPD-Link III Serializer # 方式 B命令行快速启用 echo CONFIG_DS90UB947m .config make olddefconfig逻辑说明linux-5.15.patch不仅修改drivers/media/i2c/下的文件还向drivers/media/i2c/Kconfig新增菜单项、向drivers/media/i2c/Makefile添加obj-$(CONFIG_DS90UB947) ds90ub947.o。这是标准内核模块集成流程确保make modules时能正确编译。3.2 编译与安装如何避免 “Module has no symbols” 错误驱动编译后生成drivers/media/i2c/ds90ub947.ko但直接insmod常报invalid module format。根本原因是内核配置中未启用MODULES和MODULE_UNLOAD# 编译前必须确认以下配置已启用在 .config 中 CONFIG_MODULESy CONFIG_MODULE_UNLOADy CONFIG_MODULE_FORCE_UNLOADy # 调试期强烈建议开启 CONFIG_I2C_CHARDEVy # 否则无法用 i2c-tools 调试编译与安装命令# 编译模块不编译整个内核 make modules Mdrivers/media/i2c/ # 检查模块符号表关键避免 no symbols modinfo drivers/media/i2c/ds90ub947.ko | grep -E (vermagic|depends|alias) # 正常输出应包含 # vermagic: 5.15.83 SMP mod_unload aarch64 # depends: videodev,i2c-core # alias: i2c:ds90ub947 # 安装到 modules 目录需 root sudo make modules_install Mdrivers/media/i2c/ # 加载模块需先加载依赖 sudo modprobe videodev sudo modprobe i2c-core sudo insmod /lib/modules/5.15.83/kernel/drivers/media/i2c/ds90ub947.ko参数说明modinfo输出中的vermagic字段必须与当前运行内核完全一致包括SMP、mod_unload、aarch64等标识。若显示5.15.83-rc1而你运行的是5.15.83则insmod必失败。这是内核模块签名机制的硬性要求没有后悔药。3.3 设备树编译与烧录dtc 命令的两个致命参数设备树源文件.dts修改后必须用dtc重新编译为二进制.dtb否则bootloader加载时解析失败# 编译单个 dts 文件关键参数- 插入 __symbols__ 节-H epapr 生成标准 header dtc -I dts -O dtb - -H epapr \ -o rk3566-evb.dtb \ arch/arm64/boot/dts/rockchip/rk3566-evb.dts # 验证 dtb 是否包含 ds90ub947 节点 fdtget -t s rk3566-evb.dtb /i2cff140000/ds90ub94748 compatible # 应输出ti,ds90ub947避坑dtc命令必须加-参数。不加则生成的.dtb中无__symbols__节内核启动时of_find_node_by_path(/i2cff140000/ds90ub94748)返回NULL驱动 probe 根本不执行。这是 RK 平台特有的坑全志/瑞芯微文档均未强调但实测 100% 触发。4. 避坑指南五个高频翻车现场与血泪排查路径4.1 现象dmesg | grep ds90无任何输出i2cdetect -y 3却能扫到0x48原因设备树中ds90ub94748节点未放在正确的 I2C 总线下或status okay写在父节点如i2c3而非子节点。排查# 查看内核是否解析到该节点 cat /proc/device-tree/i2cff140000/ds90ub94748/compatible # 若报 No such file or directory说明 dtb 未生效或节点路径错误 # 检查 dtb 是否被 bootloader 正确加载 dmesg | grep -i loading dtb解决确认i2c3 { ... ds90ub94748 { ... }; };结构且status okay在ds90ub94748节点内。4.2 现象dmesg显示DS90UB947: probe failed: -12ENOMEM原因devm_kzalloc()分配内存失败通常因sizeof(struct ds90ub947_dev)过大或内存碎片。但更常见的是regmap_init_i2c()失败因ds90ub947_regmap_config中max_register设错。排查# 在 probe 函数中加临时 printk printk(KERN_INFO DS90UB947: before regmap_init\n); dev-regmap devm_regmap_init_i2c(...); if (IS_ERR(dev-regmap)) { printk(KERN_ERR DS90UB947: regmap_init failed: %ld\n, PTR_ERR(dev-regmap)); return PTR_ERR(dev-regmap); }解决将max_register严格设为0xFF并确认reg_bits 8, val_bits 8。4.3 现象i2cget -y 3 0x48 0x00返回0x94但i2cget -y 3 0x48 0x01返回0xff原因芯片未退出复位态。ti,reset-gpios指向错误 GPIO或 GPIO 方向/电平配置反了GPIO_ACTIVE_LOW误写为GPIO_ACTIVE_HIGH。排查# 用万用表量 RESET 引脚电压正常应为 3.3V # 用示波器抓 RESET 波形确认有 1ms 低脉冲 # 检查 GPIO 是否被其他驱动占用 cat /sys/kernel/debug/gpio | grep gpio-12解决修正ti,reset-gpios并在ds90ub947_hw_reset()中增加msleep(2)确保复位时间。4.4 现象驱动加载成功但摄像头无图像dmesg显示DS90UB947: Link loss detected原因ti,pre-emphasis或ti,eq-level值不匹配线缆特性。长线缆1m需更高预加重短线缆0.3m需降低均衡。排查# 用调试工具读取链路状态寄存器 ./tools/ds90ub947_link_status -d /dev/i2c-3 -a 0x48 # 关键字段LINK_STATUS (0x10) bit[0]1 表示链路锁定bit[1]1 表示 CRC 错误解决按线缆长度梯度调整0x03/0x04每次rmmod/insmod后用i2cset动态写入测试。4.5 现象内核 paniclog 显示Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000原因ds90ub947_read_chip_id()中regmap_read()返回负值但未检查直接赋给u8变量导致高位扩展为0xffffffff后续指针运算越界。排查// 原始代码有缺陷 u8 id; regmap_read(dev-regmap, 0x00, id); // 若失败id 0xffffffff 0xff 0xff if (id ! 0x94) return -ENODEV; // 0xff ! 0x94但未捕获错误 // 修复后 unsigned int id_val; ret regmap_read(dev-regmap, 0x00, id_val); if (ret) return ret; if ((id_val 0xff) ! 0x94) return -ENODEV;解决所有regmap_read()调用后必须检查返回值id_val类型必须为unsigned int。5. 运行时调试与链路验证用三类工具构建闭环诊断体系驱动加载成功只是起点真正考验在于链路稳定性。我一般会用以下三类工具组合构建从寄存器层、协议层到应用层的完整验证闭环。这套方法在某工业质检项目中帮我们定位到一个隐藏 3 个月的间歇性丢帧问题——根源是0x04EQ Level在高温下漂移而非硬件故障。5.1 寄存器级ds90ub947_i2c_test工具的深度用法该工具位于tools/ds90ub947_i2c_test.c编译后生成静态可执行文件无需内核模块即可运行是硬件 bring-up 阶段的首选# 编译需交叉编译链 aarch64-linux-gnu-gcc -static -o ds90ub947_i2c_test tools/ds90ub947_i2c_test.c # 基础读写测试验证 I2C 通路 ./ds90ub947_i2c_test -d /dev/i2c-3 -a 0x48 -r 0x00 # 输出Read reg 0x00 0x94 # 批量读取关键状态寄存器0x10~0x15 ./ds90ub947_i2c_test -d /dev/i2c-3 -a 0x48 -R 0x10 0x15 # 输出0x100x01 0x110x00 0x120x00 0x130x00 0x140x00 0x150x00 # 解读0x100x01 表示 LINK_LOCKED0x110x00 表示无 CRC ERROR # 动态写入寄存器调试 EQ Level ./ds90ub947_i2c_test -d /dev/i2c-3 -a 0x48 -w 0x04 0x06技巧-R参数支持连续地址读取比循环调用-r快 10 倍。某次发现0x11CRC_ERROR_CNT在连续运行 2 小时后从0x00变为0x03立即锁定是线缆屏蔽不良而非驱动 bug。5.2 协议层ds90ub947_link_status的实时监控与阈值告警该工具专为产线老化测试设计可后台运行并按阈值触发日志# 启动监控每 500ms 读一次链路状态CRC 错误超 5 次告警 ./ds90ub947_link_status -d /dev/i2c-3 -a 0x48 -i 500 -t 5 # 输出示例 # [2024-06-15 10:23:45] LINK_LOCKED1, CRC_ERR0, SYNC_LOST0 # [2024-06-15 10:23:46] LINK_LOCKED1, CRC_ERR0, SYNC_LOST0 # [2024-06-15 10:23:47] ALERT: CRC_ERR count reached 5! Current5, Max5其核心逻辑是读取0x10LINK_STATUS、0x11CRC_ERROR_CNT、0x12SYNC_LOSS_CNT三个寄存器并做增量判断。源码中crc_err_last变量用static修饰确保跨调用保持状态——这是避免误报的关键。5.3 应用层V4L2 框架下的摄像头数据流验证DS90UB947 本身不产生 video_device但它作为 serializer必须与 deserializer如 DS90UB948配对最终由 deserializer 的 V4L2 driver 创建/dev/video0。验证链路是否真正打通最直接的方式是捕获一帧# 确认 video 设备存在且权限正确 ls -l /dev/video* # crw-rw---- 1 root video 81, 0 Jun 15 10:00 /dev/video0 # 用 v4l-utils 抓一帧YUYV 格式 v4l2-ctl --device /dev/video0 --set-fmt-videowidth1920,height1080,pixelformatYUYV v4l2-ctl --device /dev/video0 --stream-mmap --stream-count1 --stream-to/tmp/frame.yuv # 检查帧大小1920*1080*2 4,147,200 字节 ls -l /tmp/frame.yuv # -rw-r--r-- 1 root root 4147200 Jun 15 10:05 /tmp/frame.yuv血泪经验v4l2-ctl报VIDIOC_STREAMON: Invalid argument时90% 是 deserializer driver 的video_register_device()失败而非 DS90UB947 问题。此时应dmesg | grep -i video重点看 deserializer 的 probe log。从那以后我每次调试摄像头链路都强制先cat /proc/devices | grep video确认 video 主设备号已注册再查 serializer。希望帮到你。本文还有配套的精品资源点击获取