首先来明确下设计结构,不管是用HDMI,VGA还是DP,最重要的过程是把帧缓冲中的RGB数据转换成显示扫描时序。显示扫描时序这个词并不存在于显示行业,只是我认为这样说能概括问题。
来解释下显示扫描时序。先说几个名词,之后会用上:
1、像素时钟(CLK):屏幕上每个像素点显示动态显示都需要像素时钟来刷新;
2、行同步时钟(HSYNC):当显示器显示一行像素的时间;
3、场同步时钟(VSYNC):显示器显示一帧内容的时间;
4、像素数据(data):每个像素点的原始RGB数据;
找两张图来解释下:
就这样吧,重点不在这里。为什么要写这么点?我觉得恰到好处,不会的人可以得到提示去专门学一下显示控制器的相关知识。会的人也不想看太多。同智的内容看起来舒服,但是不会有成长。
PL部分的实现:
这是显示控制器的IP核,可以在ADI官网找到。注意下输入信号:
1、S_AXI是利用AXI_LITE对该控制器进行一些寄存器相关的配置。
2、hdmi_clk就是像素时钟;
3、vdma_clk是和VDMA控制器同步的时钟;
4、vdma_data是链接dma控制器输出端,也是该模块的原始数据输入;
输出信号就是之前讲的显示扫描时序,你可以在物理层接HDMI传输器,或者直接接RGB显示器。
VDMA控制器:
先看信号含义:
1、S_AXI_LITE同样是完成该控制器配置的;
2、mm2s_fsync:帧同步
3、M_AXI_MM2S:DMA数据来源;
4、M_AXI_MM2S:DMA数据流出;
以上是部分的连接图,尝试了好多次都没有好办法把矢量图粘贴上来。有需要可以邮箱联系我 [email protected] tcl脚本后再github发布。
好了,到这一步,PL部分基本完成。来看下地址分配。这个很重要,之后无论是裸金属操作还是搭载操作系统,中断号和控制器地址永远是最重要的,地址信息如下:
中断号:
完成了PL部分设计以后,开始转向PS设计。
PS会统筹所用PL以及硬件资源,最后实现内容的显示。
以上连接在zynq上的ADV7511。
1、除了视频控制信号以外,还需要一路IIC对该器件进行一些配置。因此PS代码中应该有ADV7511的初始化代码;
2、在PL总的连接图可以看出,显示控制器需要一路clk_gen,该信号受AXI总线控制;
3、视屏控制器以及VDMA,这部分代码是显卡驱动程序;
根据以上所分析的,来对设备树进行修改:
/ {
fpga_axi: fpga-axi@0 {
compatible = "simple-bus";
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
i2c@41600000 {
compatible = "xlnx,axi-iic-1.02.a", "xlnx,xps-iic-2.00.a";
reg = <0x41600000 0x10000>;
interrupt-parent = <&intc>;
interrupts = <0 58 4>;
clocks = <&clkc 15>;
clock-names = "pclk";
#address-cells = <1>;
#size-cells = <0>;
i2c_mux: i2cswitch@74 {
compatible = "nxp,pca9548";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x74>;
i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
osc@5d {
compatible = "si570";
temperature-stability = <50>;
reg = <0x5d>;
factory-fout = <156250000>;
initial-fout = <148500000>;
};
};
i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
adv7511: adv7511 {
compatible = "adi,adv7511";
reg = <0x39>, <0x3f>;
reg-names = "primary", "edid";
adi,input-depth = <8>;
adi,input-colorspace = "rgb";
adi,input-clock = "1x";
adi,clock-delay = <0>;
#sound-dai-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
adv7511_in: endpoint {
remote-endpoint = <&axi_hdmi_out>;
};
};
port@1 {
reg = <1>;
};
};
};
};
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
eeprom@54 {
compatible = "at,24c08";
reg = <0x54>;
};
};
i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
gpio@21 {
compatible = "ti,tca6416";
reg = <0x21>;
gpio-controller;
#gpio-cells = <2>;
};
};
i2c@4 {
#address-cells = <1>;
#size-cells = <0>;
reg = <4>;
rtc@54 {
compatible = "nxp,pcf8563";
reg = <0x51>;
};
};
};
};
axi_vdma_0: axivdma@43000000 {
#address-cells = <1>;
#size-cells = <1>;
#dma-cells = <1>;
compatible = "xlnx,axi-vdma-1.00.a";
reg = <0x43000000 0x1000>;
xlnx,num-fstores = <0x3>;
dma-channel@43000000 {
compatible = "xlnx,axi-vdma-mm2s-channel";
interrupts = <0 59 0x4>;
xlnx,datawidth = <0x40>;
xlnx,genlock-mode = <0x0>;
xlnx,include-dre = <0x0>;
};
};
hdmi_clock: axi-clkgen@0x43C10000 {
compatible = "adi,axi-clkgen-2.00.a";
reg = <0x43C10000 0x10000>;
#clock-cells = <0>;
clocks = <&clkc 16>;
};
axi_hdmi@0x43C00000 {
compatible = "adi,axi-hdmi-tx-1.00.a";
reg = <0x43C00000 0x10000>;
dmas = <&axi_vdma_0 0>;
dma-names = "video";
clocks = <&hdmi_clock>;
adi,is-rgb;
port {
axi_hdmi_out: endpoint {
remote-endpoint = <&adv7511_in>;
};
};
};
// axi_spdif_tx_0: axi-spdif-tx@75c00000 {
// compatible = "adi,axi-spdif-tx-1.00.a";
// reg = <0x75c00000 0x1000>;
// dmas = <&dmac_s 0>;
// dma-names = "tx";
// clocks = <&clkc 15>, <&audio_clock>;
// clock-names = "axi", "ref";
// #sound-dai-cells = <0>;
};
};
audio_clock: audio_clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <12288000>;
};
adv7511_hdmi_snd {
compatible = "simple-audio-card";
simple-audio-card,name = "HDMI monitor";
simple-audio-card,widgets =
"Speaker", "Speaker";
simple-audio-card,routing =
"Speaker", "TX";
simple-audio-card,dai-link@0 {
format = "spdif";
cpu {
sound-dai = <&axi_spdif_tx_0>;
frame-master;
bitclock-master;
};
codec {
sound-dai = <&adv7511>;
};
};
};
};
设备树的修改比较容易,就是根据之前PL部分的地址已经中断号。IIC控制器的设备树要结合自己的硬件平台来进行修改,我这里使用的是软核IIC控制器+IIC多路器来对adv7511进行控制。
在调试的时候要首先关注这里,CPU通过IIC控制器会读取液晶显示器的EDID,该驱动程序注册了debugfs,可以直接查看到设备的edid以及时钟等信息。debugfs使用如下:
1、配置内核,使其支持debugfs Kernelhacking --->[*]Debug Filesystem;
2、mount -t debugfs none /mnt;
3、打开挂在的目录;
HDMI识别显示器的过程有必要啰嗦一下。当显示器连接到HDMI connector 上时,已有一个hotplug信号。该信号表征HDMI显示器已经连接,硬件测试时候要关注该信号。当ADV7511拿到该信号以后,就开始读取液晶显示器的EDID,(EDID就是告诉显示控制器自己的信息,比如分辨率,backsacn time等)。显示控制器拿到这些信息以后,显示控制器会发出与之匹配的信号。
如果这一步没有问题,示波器就能够测量到clk,hsync,vsync信号,显示器就可以正常工作。