简介:Sil9136是一款高集成度USB转串行桥接芯片,在嵌入式开发中常用于扩展UART/SPI/I2C接口。这份zip资料围绕Sil9136芯片,整合官方参考手册与可移植驱动源码,面向硬件设计人员和嵌入式软件开发者,解决芯片选型评估、寄存器配置、驱动移植及调试中的常见问题。包体共24个文件,约13.73MB,包含多份PDF芯片手册与编程参考、C/H格式驱动源码、编译生成的ko/模块文件,以及Makefile、配置脚本和测试样例,兼顾源码阅读与直接编译验证。已有571人学习下载。通过该资源,读者可以快速掌握Sil9136的初始化流程、接口参数配置和数据传输方法,参照驱动源码理解底层寄存器读写与中断处理,缩短在物联网模块、工业控制等场景中的开发周期,是一份较为完整的工程参考资料。
1. 拿到 sil9136 驱动源码,先别急着移植
做 HDMI 视频方案的同学大概都经历过这种场面:板子拿回来,参考设计照抄,上电却发现 HDMI 信号死活检测不到,I2C 读寄存器全是 0xFF,示波器敲了半天怀疑是芯片虚焊。实际上很多情况下不是硬件问题,而是软件初始化时序没按芯片手册走。sil9136 就是这类容易让人翻车的 HDMI 接收芯片,这份「参考手册 + 驱动源码」的压缩包,就是把数据手册和可编译的 Linux 驱动放在一起,让做驱动移植的人不再对着 PDF 一页页翻寄存器。适合嵌入式驱动工程师、视频方案集成商,以及所有准备把 HDMI 输入接到 MIPI/LVDS 显示模组上的开发者。
2. 资源拆解:手册与驱动源码如何配合使用
2.1 sil9136 在视频链路中的角色
sil9136 是一颗 HDMI 1.4 接收器芯片,市面上常见于车载娱乐系统、广告机、工业显示模组这类需要把 HDMI 信号转成内部显示接口的场景。它的上游是 HDMI 源设备,下游则对接 MIPI DSI、LVDS 或 RGB 并口屏,中间还夹着 EDID 读取和 HDCP 处理。
这颗芯片的控制面是 I2C 总线,配置寄存器、读取中断状态、获取 EDID 都走这条通道。数据面则是完整的 HDMI 接收前端,包含时钟恢复、通道均衡、音视频分离等模块。驱动的工作本质上是做两件事:把芯片配置到正确的接收状态,以及把接收到的视频时序上报给系统。
从驱动架构上看,sil9136 在 Linux 下通常挂成一个 I2C 客户端设备,probe 阶段完成芯片识别和初始化,然后注册中断处理函数响应热插拔事件。V4L2 子设备或者 DRM bridge 框架下都能挂,取决于你内核版本和显示链路怎么搭。
2.2 压缩包内两大部分怎么分工
参考手册和驱动源码不是两份孤立材料,它们的关系是「寄存器定义」和「寄存器操作」的关系。手册里有完整的寄存器映射表、上电时序图、EDID 缓冲区的访问方式,而驱动源码则是这一切的工程化落地——把手册里的操作步骤写成可执行的 C 代码。
我拿到这类压缩包的习惯是先在手册里定位三块内容:芯片 ID 寄存器的地址和复位默认值、初始化序列要求的最短延时、中断状态位对应的清除方式。然后在源码里对着这三块找对应实现。如果手册里说复位后需要等待至少 10ms 才能访问寄存器,但源码里只等了 1ms,那这就是一个隐患,后续稳定性问题多半从这里来。
2.3 拿到资源后的第一遍操作清单
不要急着编译和上板,先做下面几步,这几步能帮你省掉后面大部分排查时间。
第一步,打开原理图,确认 sil9136 的 I2C 地址引脚上下拉配置,这在手册里有明确说明,地址引脚电平组合决定了从机地址。第二步,在驱动源码里搜i2c相关的地址宏定义,和原理图对照,确认一致。第三步,确认中断引脚接到了处理器的哪个 GPIO,这个 GPIO 是否支持唤醒。第四步,看手册里芯片上电到 HPD 输出有效的时序参数,把这个值记下来,待会儿看驱动源码里的延时是否满足。
提示:这一步整理出来的对照关系,直接决定后面调试时你是看手册还是看代码,还是两边都不对。
3. 参考手册精读:从寄存器映射到 EDID 处理
3.1 寄存器映射先把地址空间理清
sil9136 的寄存器空间通过 I2C 访问,手册里会把不同功能块分配到不同地址段。就这类 HDMI 接收芯片的常见布局来说,地址空间大致可以分成这么几块:芯片 ID 和版本信息、主控制寄存器、中断状态与使能、视频输入状态、EDID 相关控制、音频状态。
我把最常见的关键寄存器按用途整理成下表,具体偏移地址以手册为准,这里讲的是「看手册时应该留意什么」:
| 寄存器类别 | 需要关注的内容 | 典型用途 |
|---|---|---|
| ID 寄存器 | 芯片型号和版本默认值 | 驱动 probe 时校验芯片存在 |
| 主控制 | 软复位、PLL 使能、输出使能 | 初始化序列的核心操作点 |
| 中断状态 | Hot Plug 变化、EDID 请求、视频格式变化 | 热插拔和格式感知 |
| 视频状态 | 输入时序锁定状态、色彩空间指示 | 判断 HDMI 信号是否有效 |
| EDID 控制 | EDID 缓冲区读写指针、使能位 | 向上游提供 EDID 内容 |
读手册时有个容易忽略的细节:很多功能寄存器不是简单写个值就能生效,而是需要先置某个使能位,等芯片内部状态机跑完,再读状态位确认。驱动里大量read-modify-write操作,就是干这个事。
3.2 上电复位与初始化序列的时序要求
手册里通常会给出一个推荐的初始化序列,这类 HDMI 接收芯片的序列一般都遵循这个节奏:先复位,再配置 PLL,然后等待时钟稳定,接着配置视频输出接口,最后使能中断。每一步之间都有明确的延时要求。
参考这类芯片的通用初始化流程,初始化序列的骨架长这样:
/* 步骤1: 软复位 */ reg_write(dev, REG_MAIN_CTRL, 0x01); mdelay(20); /* 手册要求复位后等待 */ /* 步骤2: 解除复位并等待芯片稳定 */ reg_write(dev, REG_MAIN_CTRL, 0x00); mdelay(50); /* 等待内部 PLL 锁定 */ /* 步骤3: 配置视频输出端 */ reg_write(dev, REG_OUTPUT_CFG, OUTPUT_MIPI_2LANE); mdelay(10); /* 步骤4: 使能相关中断 */ reg_write(dev, REG_INT_ENABLE, HPD_CHANGE | EDID_REQUEST);这段代码看起来平淡无奇,但每个延时都有讲究。第二步的 50ms 是给内部 PLL 和时钟恢复电路建链用的,缩短到 10ms 以内会出现一种奇怪现象:寄存器能读能写,但 HDMI 信号就是锁不住。这就是典型的「初始化序列时序没给够」,也是这类芯片调试中最常见的坑之一。
3.3 EDID 处理的完整流程
EDID 是 HDMI 链路里绕不开的一环,sil9136 的 EDID 处理,简单说就是芯片内部有一块缓冲区,系统软件需要把显示模组的 EDID 数据写进这块缓冲区,然后向下游 HDMI 源设备提供 EDID 响应。
驱动里 EDID 相关操作的常见流程是:检测到上游有 EDID 读取请求(中断状态位置位)→ 软件从系统 EDID 存储区读取数据 → 按块(128 字节为一块)写入芯片 EDID 缓冲区 → 写入完成后使能 EDID 响应。
这里有一个实际操作上的细节:EDID 缓冲区不是一次性写完的,而是按块写,每写完一块要等芯片处理完再写下一块。手册里对每块之间的间隔通常有明确要求,写太快芯片会丢弃数据,写太慢上游又会超时。驱动里常见做法是每写一块后轮询确认状态位,确认完成再继续。
4. 驱动源码解读:从 I2C 读写到热插拔检测
4.1 驱动目录结构与编译方式
驱动源码的目录结构通常遵循 Linux 内核驱动源码的常规布局,常见结构大致如下:
sil9136_drv/ ├── Makefile ├── Kconfig ├── sil9136.c # 主驱动文件,包含 probe/remove/中断处理 ├── sil9136.h # 寄存器定义、结构体声明 ├── sil9136_i2c.c # I2C 读写封装 ├── sil9136_edid.c # EDID 读写逻辑 └── sil9136_dt.h # 设备树配置相关宏编译方式通常有两种:一种是编进内核,靠 Kconfig 和 Makefile 完成;另一种是编成内核模块,单独在源码目录下执行make -C /lib/modules/$(uname -r)/build M=$(pwd) modules。如果你是在交叉编译环境,make命令里还需要加上 ARCH 和 CROSS_COMPILE 变量。
4.2 寄存器的 I2C 读写封装
I2C 读写是所有功能的基础,这段封装的质量直接决定驱动稳定性。以下是一个常见的封装示例:
static int sil9136_i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val) { struct i2c_msg msg[2]; u8 buf; int ret; /* 先写寄存器地址 */ msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = ® /* 再读一个字节数据 */ msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = 1; msg[1].buf = &buf; ret = i2c_transfer(client->adapter, msg, 2); if (ret != 2) { dev_err(&client->dev, "read reg 0x%02x failed: %d\n", reg, ret); return -EIO; } *val = buf; return 0; }这段代码有两个值得注意的点。第一,i2c_transfer的返回值是成功传输的消息数量,所以判断条件是ret != 2,而不是ret < 0。第二,写地址和读数据拆成了两个 message,这是 I2C 读操作的常规组织方式,需要确认芯片支持这种 repeated start 的读时序,sil9136 这类 HDMI 接收芯片是支持的。
写入操作类似,只是不需要第二个 message,一个i2c_master_send就能完成,但要注意很多寄存器是只读状态位,写入前最好先读回确认当前值,避免破坏其他位。驱动源码中比较规范的做法会封装一个update_bits类型的函数,先读后改再写。
4.3 芯片初始化与热插拔检测
probe 函数里的初始化逻辑,是一份驱动源码里信息密度最高的部分,它把手册里的上电流程翻译成了代码。下面这个示例展示了典型的初始化骨架:
static int sil9136_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sil9136_dev *dev; u8 chip_id; int ret; dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->client = client; i2c_set_clientdata(client, dev); /* 读取芯片 ID,确认设备在线 */ ret = sil9136_i2c_read_reg(client, REG_CHIP_ID, &chip_id); if (ret < 0 || chip_id != SIL9136_CHIP_ID_VAL) { dev_err(&client->dev, "chip id mismatch: 0x%02x\n", chip_id); return -ENODEV; } /* 执行初始化序列 */ ret = sil9136_init_hw(dev); if (ret < 0) return ret; /* 注册中断,响应热插拔 */ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, sil9136_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sil9136", dev); if (ret < 0) { dev_err(&client->dev, "request irq failed: %d\n", ret); return ret; } dev_info(&client->dev, "sil9136 initialized\n"); return 0; }probe 里建议结合设备树配置、寄存器操作、中断申请三个维度理解。芯片 ID 校验是驱动识别硬件的第一步,如果读回来不是预期值,那后续所有操作没有意义,不需要继续往下执行。中断触发方式是IRQF_TRIGGER_FALLING,这是从硬件设计上决定的,需要参考原理图上 HPD 信号如何接入处理器 GPIO;有些板子 HPD 是低有效,有些是高有效,触发方式不对会导致热插拔事件完全丢失。
热插拔检测的中断处理函数,常见实现是读中断状态寄存器,判断是 HPD 变化还是 EDID 请求,然后调度工作队列去处理;工作队列里再执行完整的链路协商流程。不清楚的地方先看板子原理图和设备树里interrupts属性是怎么写的,再用devm_request_threaded_irq的线程化处理来避免在中断上下文做耗时操作。
4.4 EDID 读取与视频时序配置
EDID 读取逻辑是驱动里绕不开的模块。下面是一段简化后的 EDID 块读取实现:
static int sil9136_read_edid_block(struct sil9136_dev *dev, u8 block, u8 *buf) { u8 addr = block * 128; u8 status; int ret, i; /* 写 EDID 块起始地址 */ ret = sil9136_i2c_write_reg(dev, REG_EDID_ADDR, addr); if (ret < 0) return ret; /* 等待芯片准备数据 */ for (i = 0; i < 100; i++) { ret = sil9136_i2c_read_reg(dev, REG_EDID_STATUS, &status); if (ret < 0) return ret; if (status & EDID_DATA_READY) break; usleep_range(1000, 2000); } if (i >= 100) return -ETIMEDOUT; /* 连续读取 128 字节 */ return sil9136_i2c_read_bulk(dev, REG_EDID_DATA, buf, 128); }REG_EDID_ADDR写的是块内偏移,按块读取时地址按 128 字节递增。REG_EDID_STATUS状态位的作用是避免数据还没准备好就开读,这里的超时轮询上限是 100 次,每次等 1-2ms,总共最多等 200ms;如果读到的是全 0xFF,通常是时序问题或者上游显示器还没真正输出有效 EDID。
驱动解析 EDID 后,会把首选时序提取出来配置到输出端。这时需要同时核对参考手册的输出格式配置寄存器,确认像素时钟和通道数设置符合面板要求。
5. 避坑指南:sil9136 调试中的常见问题与排查线路
5.1 I2C 探测不到设备,写寄存器返回错误
现象是i2cdetect扫描不到设备地址,或者驱动 probe 阶段i2c_transfer返回-EIO。这个现象有个迷惑性:有时地址写对就能读到,有时完全黑屏,玄学一样。
原因多数是 I2C 地址配置脚电平不对,或者驱动里用的地址值本身就不是芯片实际响应的地址。另一个常见原因是地址位格式混乱:7 位地址和 8 位地址搞混。很多 HDMI 接收芯片的 8 位地址是 0x72,对应 7 位地址就是 0x39,差一位左移,写错就完全找不到设备。
解决办法是先看原理图上地址引脚的上下拉,再打开手册确认对应关系,最后用i2cdetect -y <bus>实测。驱动源码里如果地址需要可配,建议在设备树reg属性里指定,不要写死在代码里。
5.2 芯片能识别但 HDMI 信号锁不住,读视频状态位一直无信号
现象是驱动 probe 成功、寄存器读写正常,但视频状态寄存器始终显示没有信号锁定,示波器看 HDMI 差分线上有波形。
原因是初始化序列执行不完整,或者各步骤之间的延时没给够。这类芯片的接收前端建立链路需要时间,特别是 PLL 锁定那一步,延时太短会导致前端没有进入正确的接收状态。还有一种可能:输出端接口(MIPI/LVDS)配置和实际硬件不符,导致内部状态机卡在等待输出的状态。
解决方法是严格对照手册的初始化序列重新梳理驱动里的每一步,重点检查两个延时:复位释放后的等待时间、PLL 配置后的锁定时间。不要自作聪明缩短,按手册来。
5.3 HPD 中断一直不触发,插拔 HDMI 线没有反应
现象是拔插 HDMI 线,驱动里的中断处理函数没有执行,但寄存器里 HPD 状态位通过轮询能读到变化。
这通常不是芯片问题而是中断通路问题。常见原因有两个:第一,处理器 GPIO 的中断触发方式配置不对,HPD 信号有效电平与设备树里interrupts属性描述的不一致;第二,GPIO 内部没有配置上拉或下拉,HPD 线浮空时电平不定,导致边沿不干净。
排查先用cat /proc/interrupts确认中断有没有注册上,再用示波器量 GPIO 引脚电平变化,最后对照硬件修改设备树的interrupts属性或驱动里的触发标志。
5.4 EDID 读取出来全是 0xFF
现象是上游设备能读到 EDID,但内容全是一字节 0xFF,显示器识别成未知设备。
原因是读取时机太早。芯片上电后内部需要时间加载配置和准备 EDID 缓冲区,如果驱动在 probe 阶段立刻读 EDID,数据还没准备好,读回来的就是总线空闲电平对应的 0xFF。还有一种情况是 EDID 数据写入芯片缓冲区时没有按块等待,芯片丢弃了部分数据。
解决方法是把 EDID 读取放在初始化完成且状态就绪之后,读取前先轮询 EDID 相关状态位,别相信固定延时。调试时可以从i2cdump看缓存区内容是否可见,确认硬件通路没问题。
5.5 内核模块加载报Unknown symbol或版本不匹配
现象是insmod时提示Unknown symbol,或者直接报version magic不匹配拒绝加载。
原因是驱动源码编译时使用的内核头文件和当前运行内核版本不一致,或者驱动依赖的其他内核符号当前内核没有导出。
解决方法是确认目标内核的版本和源码版本,重新编译前先把Module.symvers从内核源码目录拷贝到驱动源码目录,或者直接把驱动编进内核镜像而不是用模块方式加载。交叉编译环境下还要核对 ARCH 参数是否和目标板一致。
6. 二次开发:改分辨率、改地址与调试技巧
6.1 修改输出分辨率
sil9136 的输出分辨率由初始化和视频输出配置共同决定,推荐先看参考手册里的输出格式配置表,找到面板对应的时序参数。然后把驱动源码里初始化序列中的输出配置寄存器值改成目标分辨率对应的一组配置,改了配置不要忘记清 PLL 重新锁定。改完后用一个支持该分辨率的 HDMI 源去实测验证,不要只看寄存器值。
6.2 调整 I2C 从机地址
如果板子上同时挂了多颗 sil9136,或者地址和别的设备冲突,调整从机地址就成了刚需。硬件上通过地址引脚的电平组合决定,软件上改设备树reg属性即可:
&i2c2 { sil9136@39 { compatible = "厂商,sil9136"; reg = <0x39>; interrupt-parent = <&gpio1>; interrupts = <3 IRQ_TYPE_EDGE_FALLING>; }; };设备树里reg写的是 7 位地址,驱动里client->addr会自动拿到正确值。如果驱动源码里有硬编码地址,记得一并改掉,否则设备树配了也白搭。
6.3 调试技巧
驱动开发阶段加打印是最高效的手段。建议在 probe、中断处理、EDID 读取三个关键路径上加dev_info配合寄存器值打印。调试 I2C 问题时,用i2cdetect -y -r <bus>扫描地址,用i2cdump -y <bus> <addr>看寄存器内容。这两个命令能帮你快速确认是驱动问题还是硬件问题。
从那次以后,我每次在类似 HDMI 接收芯片上做驱动移植,都强制走一遍:先读手册确认初始化时序,再对照源码确认延时和寄存器操作,最后用 i2c-tools 实测验证,这套流程下来基本不会在低级问题上浪费超过半天时间。希望帮到你。
本文还有配套的精品资源,点击获取