ZYNQ #EC1 PL端模擬HDMI輸出,i2c接入PCA9548複用器後設備樹的分析

本文爲番外篇,分析了在前面 ZYNQ7000 #1 - PL端模擬HDMI信號輸出環境下的Linux界面顯示 基礎上,當HDMI的I2c總線非直接接入而是通過一個 i2c 選擇器後,如何進行設備樹修改,進行哪些相關資料參考。

 

目錄

1 - 楔子

2 - 參考與借鑑

2.1 - 參考1 : armada-385-turris-omnia.dts

2.2 - 參考2:imx6q-b850v3.dts

2.3 - 參考3:Digilent的 digillent_encoder文檔

2.4 - 參考4:設備樹binding文檔中的i2c-mux.txt與i2c-mux-pca954x.txt

3 - 硬件搭建

4 - 編寫

5 - 註釋與解析


1 - 楔子

米聯客MZ7035FD的HDMI接口上,其用於與設備通訊獲取設備屏幕參數EDID的i2c接口,並非直接從PL的引腳出來佈線到HDMI-A接口上,而是經過了一個i2c複用器PCA9548APW

這裏我們希望能夠使用我們之前部署的 Digilent提供的視頻解碼驅動(encoder)和時鐘驅動(clk),來實現自動識別輸出分辨率,而不用每次更改PL端的代碼並重新編譯。

參考前面的文章 ZYNQ7000 #1 - PL端模擬HDMI信號輸出環境下的Linux界面顯示 ,我們知道,只需要在設備樹中將鏈接到HDMI-A接口的 i2c 主設備作爲參數,輸入給 amba_pl 總線下的 hdmi_encoder 節點、xilinx_drm 節點的 edid-i2c 即可。

參考下面的代碼,注意加粗部分。有部分刪減,詳情請參考上面鏈接文章中的代碼:

&i2c0 {
    clock-frequency = <100000>;
};
 
&amba_pl {
 
    hdmi_encoder_0:hdmi_encoder {
        compatible = "digilent,drm-encoder";
        digilent,edid-i2c = <&i2c0>;
    };
 
    xilinx_drm {
        compatible = "xlnx,drm";
        xlnx,vtc = <&v_tc_0>;
        xlnx,connector-type = "HDMIA";
        xlnx,encoder-slave = <&hdmi_encoder_0>;
        clocks = <&axi_dynclk_0>;
        dglnt,edid-i2c = <&i2c0>;
        planes {
            xlnx,pixel-format = "rgb888";
            plane0 {
                dmas = <&axi_vdma_0 0>;
                dma-names = "dma";
            };
        };
    };
};
 

這段設備樹代碼是在黑金板子上使用的,在 i2c0 直連的時候,一切都顯得那麼簡單,但是在米聯客這塊板子上,我們應該如何修改設備樹代碼,使得支持 PCA9548 這個 i2c 複用器呢?

2 - 參考與借鑑

2.1 - 參考1 : armada-385-turris-omnia.dts

我們可以參考別人的設備樹,這裏我參考的設備樹(armada-385-turris-omnia.dts)如下,有部分刪減:

&i2c0 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c0_pins>;
	status = "okay";

	i2cmux@70 {
		compatible = "nxp,pca9547";
		#address-cells = <1>;
		#size-cells = <0>;
		reg = <0x70>;
		status = "okay";
...

		i2c@4 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <4>;

			/* routed to SFP+ */
		};
...
	};
};

2.2 - 參考2:imx6q-b850v3.dts

查看 imx6q-b850v3.dts 設備樹,有刪減。

&i2c2 {
	pca9547_ddc: mux@70 {
		compatible = "nxp,pca9547";
		reg = <0x70>;
		#address-cells = <1>;
		#size-cells = <0>;

		mux2_i2c1: i2c@0 {
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <0x0>;
		};

...
	};
};

&hdmi {
	ddc-i2c-bus = <&mux2_i2c1>;
};

在 imx6q-b850v3.dts 的頂層包含 imx6q.dtsi 有如下代碼

&hdmi {
	compatible = "fsl,imx6q-hdmi";

	port@2 {
		reg = <2>;

		hdmi_mux_2: endpoint {
			remote-endpoint = <&ipu2_di0_hdmi>;
		};
	};

	port@3 {
		reg = <3>;

		hdmi_mux_3: endpoint {
			remote-endpoint = <&ipu2_di1_hdmi>;
		};
	};
};

查看 Document 下的設備樹的binding文檔,找到 imx6q-hdmi 的相關文檔(Documentation/devicetree/bindings/display/imx/hdmi.txt)

Freescale i.MX6 DWC HDMI TX Encoder
===================================

The HDMI transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
with a companion PHY IP.

These DT bindings follow the Synopsys DWC HDMI TX bindings defined in
Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt with the
following device-specific properties.


Required properties:

- compatible : Shall be one of "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi".
- reg: See dw_hdmi.txt.
- interrupts: HDMI interrupt number
- clocks: See dw_hdmi.txt.
- clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt.
- ports: See dw_hdmi.txt. The DWC HDMI shall have between one and four ports,
  numbered 0 to 3, corresponding to the four inputs of the HDMI multiplexer.
  Each port shall have a single endpoint.
- gpr : Shall contain a phandle to the iomuxc-gpr region containing the HDMI
  multiplexer control register.

Optional properties

- ddc-i2c-bus: The HDMI DDC bus can be connected to either a system I2C master
  or the functionally-reduced I2C master contained in the DWC HDMI. When
  connected to a system I2C master this property contains a phandle to that
  I2C master controller.

可以看到,ddc-i2c-bus 要賦值的應該是一個 i2c 主設備。

這份參考指明瞭可以直接從 i2c-mux 下的分枝直接引用爲 i2c 設備

2.3 - 參考3:Digilent的 digillent_encoder文檔

可以在這裏(https://github.com/Digilent/linux-digilent/blob/master/Documentation/devicetree/bindings/drm/xilinx/digilent_encoder.txt)查看我們需要用的digilent_encoder的相關文檔,文檔中這樣寫到

Device-Tree bindings for Digilent DRM Encoder Slave

This driver provides support for VGA and HDMI outputs on Digilent FPGA boards. The
VGA or HDMI port must be connected to a Xilinx display pipeline via an axi2vid
IP core.

Required properties:
 - compatible: Should be "digilent,drm-encoder".

Optional properties:
 - digilent,edid-i2c: The I2C device connected to the DDC bus on the video connector.
                      This is used to obtain the supported resolutions of an attached 
                      monitor. If not defined, then a default set of resolutions is used
                      and the display will initialize to the closest available size 
                      specified by vpref and hpref. Note most VGA connectors on Digilent 
                      boards do not have the DDC bus routed out. 

2.4 - 參考4:設備樹binding文檔中的i2c-mux.txt與i2c-mux-pca954x.txt

在 Documentation/devicetree/bindings/i2c下我們可以看到這兩篇相關文檔

i2c-mux.txt

Common i2c bus multiplexer/switch properties.

An i2c bus multiplexer/switch will have several child busses that are
numbered uniquely in a device dependent manner.  The nodes for an i2c bus
multiplexer/switch will have one child node for each child bus.

Optional properties:
- #address-cells = <1>;
   This property is required is the i2c-mux child node does not exist.

- #size-cells = <0>;
   This property is required is the i2c-mux child node does not exist.

- i2c-mux
   For i2c multiplexers/switches that have child nodes that are a mixture
   of both i2c child busses and other child nodes, the 'i2c-mux' subnode
   can be used for populating the i2c child busses.  If an 'i2c-mux'
   subnode is present, only subnodes of this will be considered as i2c
   child busses.

Required properties for the i2c-mux child node:
- #address-cells = <1>;
- #size-cells = <0>;

Required properties for i2c child bus nodes:
- #address-cells = <1>;
- #size-cells = <0>;
- reg : The sub-bus number.

Optional properties for i2c child bus nodes:
- Other properties specific to the multiplexer/switch hardware.
- Child nodes conforming to i2c bus binding


Example :

    /*
       An NXP pca9548 8 channel I2C multiplexer at address 0x70
       with two NXP pca8574 GPIO expanders attached, one each to
       ports 3 and 4.
     */

    mux@70 {
        compatible = "nxp,pca9548";
        reg = <0x70>;
        #address-cells = <1>;
        #size-cells = <0>;

        i2c@3 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <3>;

            gpio1: gpio@38 {
                compatible = "nxp,pca8574";
                reg = <0x38>;
                #gpio-cells = <2>;
                gpio-controller;
            };
        };
        i2c@4 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <4>;

            gpio2: gpio@38 {
                compatible = "nxp,pca8574";
                reg = <0x38>;
                #gpio-cells = <2>;
                gpio-controller;
            };
        };
    };

i2c-mux-pca954x.txt

* NXP PCA954x I2C bus switch

Required Properties:

  - compatible: Must contain one of the following.
    "nxp,pca9540", "nxp,pca9542", "nxp,pca9543", "nxp,pca9544",
    "nxp,pca9545", "nxp,pca9546", "nxp,pca9547", "nxp,pca9548"

  - reg: The I2C address of the device.

  The following required properties are defined externally:

  - Standard I2C mux properties. See i2c-mux.txt in this directory.
  - I2C child bus nodes. See i2c-mux.txt in this directory.

Optional Properties:

  - reset-gpios: Reference to the GPIO connected to the reset input.
  - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
    children in idle state. This is necessary for example, if there are several
    multiplexers on the bus and the devices behind them use same I2C addresses.
  - interrupt-parent: Phandle for the interrupt controller that services
    interrupts for this device.
  - interrupts: Interrupt mapping for IRQ.
  - interrupt-controller: Marks the device node as an interrupt controller.
  - #interrupt-cells : Should be two.
    - first cell is the pin number
    - second cell is used to specify flags.
    See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt

Example:

    i2c-switch@74 {
        compatible = "nxp,pca9548";
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0x74>;

        interrupt-parent = <&ipic>;
        interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
        interrupt-controller;
        #interrupt-cells = <2>;

        i2c@2 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <2>;

            eeprom@54 {
                compatible = "at,24c08";
                reg = <0x54>;
            };
        };

        i2c@4 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <4>;

            rtc@51 {
                compatible = "nxp,pcf8563";
                reg = <0x51>;
            };
        };
    };

綜上,我們可以知道:

  • 要向 digilent_encoder 端點提供的參數應當是一個 i2c 主設備(總線控制),其通過HDMI線路連接到對接的HDMI顯示設備芯片上,內核通過該總線獲取對接設備的EDID。
  • 在 i2c-mux 下子節點可以直接作爲 i2c 節點設備給其他設備引用;其下的子總線可以直接作爲 i2c 總線給其他設備用

3 - 硬件搭建

這裏我使用的是米聯客MZ7035FD開發板,參考之前的ZYNQ7000 #1 - PL端模擬HDMI信號輸出環境下的Linux界面顯示,我們將digilent的兩個需要用到的IP放入我們的FPGA框圖設計中

添加引腳約束文件,這裏主要是爲了將TMDS信號引出到板子的HDMI接口上。注意引腳的電平問題,不同的硬件環境需要進行相關的修改,不同的引腳也只支持不同的電平

set_property IOSTANDARD LVDS [get_ports TMDS_clk_p]
set_property IOSTANDARD LVDS [get_ports {TMDS_data_p[0]}]
set_property IOSTANDARD LVDS [get_ports {TMDS_data_p[1]}]
set_property IOSTANDARD LVDS [get_ports {TMDS_data_p[2]}]
set_property IOSTANDARD LVCMOS18 [get_ports hdmi_ddc_scl_io]
set_property IOSTANDARD LVCMOS18 [get_ports hdmi_ddc_sda_io]

#package pin
set_property PACKAGE_PIN J14 [get_ports TMDS_clk_p]
set_property PACKAGE_PIN K13 [get_ports {TMDS_data_p[0]}]
set_property PACKAGE_PIN G14 [get_ports {TMDS_data_p[1]}]
set_property PACKAGE_PIN K15 [get_ports {TMDS_data_p[2]}]
set_property PACKAGE_PIN A15 [get_ports hdmi_ddc_scl_io]
set_property PACKAGE_PIN A14 [get_ports hdmi_ddc_sda_io]

#bit compress
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

4 - 編寫

假設我們的這個 PCA9548 是掛載在 pl 引出的 i2c0上,PCA9548 的地址如上面的原理圖所示,三個地址位都被拉到了地,因此它在 i2c0 設備上的地址應當是 01110000 = 0x70 (七位數據位)。我們需要用到的 HDMI 的 i2c 接到了 PCA9548 的第四個分位上。

一葫蘆畫瓢,我們可以寫出下面的設備樹代碼,作爲 system-user.dtsi 。着重看 &i2c0 、hdmi_encoder 與 xilinx_drm 節點:

/include/ "system-conf.dtsi"
 
/ {
};
 
&i2c0 {
	clock-frequency = <100000>;
	status = "okay";

	i2cmux@70 {
		compatible = "nxp,pca9548";
		#address-cells = <1>;
		#size-cells = <0>;
		reg = <0x70>;
		status = "okay";
		
		i2cH1:i2chdmi@4{
			#address-cells = <1>;
			#size-cells = <0>;
			reg = <4>;

			/* routed to HDMI connector (H1) */
		};
	};
};

&amba_pl {
	hdmi_encoder_0:hdmi_encoder {
		compatible = "digilent,drm-encoder";
		digilent,edid-i2c = <&i2cH1>;
	};
 
	xilinx_drm {
		compatible = "xlnx,drm";
		xlnx,vtc = <&v_tc_0>;
		xlnx,connector-type = "HDMIA";
		xlnx,encoder-slave = <&hdmi_encoder_0>;
		clocks = <&axi_dynclk_0>;
		dglnt,edid-i2c = <&i2cH1>;
		planes {
			xlnx,pixel-format = "rgb888";
			plane0 {
				dmas = <&axi_vdma_0 0>;
				dma-names = "dma";
			};
		};
	};
};

&axi_dynclk_0 {
	compatible = "digilent,axi-dynclk";
	#clock-cells = <0>;
	clocks = <&clkc 15>;
};

&v_tc_0 {
	compatible = "xlnx,v-tc-5.01.a";
};

5 - 註釋與解析

這裏加一段部分分析與註釋

&i2c0 {                                                                          /* PCA9548掛載在了i2c0上 */
    clock-frequency = <100000>;
    status = "okay"

    i2cmux@70 {                                                           /* PCA9548在i2c0上的地址是0x70 */
        compatible = "nxp,pca9548";                            /* 驅動適配 */
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0x70>;                                                       /* 地址0x70 */
        status = "okay";
        
        i2cH1:i2chdmi@4{                                                /* 我們需要用的只有4號分位 */
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <4>;                                                         /* 4 */

            /* routed to HDMI connector (H1) */
        }
    }
};

&amba_pl {
......

    hdmi_encoder_0:hdmi_encoder {
        compatible = "digilent,drm-encoder";
        digilent,edid-i2c = <&i2cH1>;                                 /* 注意引用 */
    };
 
    xilinx_drm {
        compatible = "xlnx,drm";
        xlnx,vtc = <&v_tc_0>;
        xlnx,connector-type = "HDMIA";
        xlnx,encoder-slave = <&hdmi_encoder_0>;
        clocks = <&axi_dynclk_0>;
        dglnt,edid-i2c = <&i2cH1>;                                     /* 注意引用 */
        planes {
            xlnx,pixel-format = "rgb888";
            plane0 {
                dmas = <&axi_vdma_0 0>;
                dma-names = "dma";
            };
        };
    };
};

以上就是設備樹的修改,記得在內核編譯時將 PCA954x 編譯入內核

6 - 接上hdmi屏

這裏有幾個注意的地方

  1. 參考我的這篇文章 ZYNQ7000 #1 - PL端模擬HDMI信號輸出環境下的Linux界面顯示 裏,修改99-fbdev.conf ,爲xorg指定framebuffer
  2. 可以參考 基於ubuntu-base進行根文件系統的修改與打包 ,讓xorg開機啓動的腳本。如果手動操作,記得先載入DISPLAY變量,再喚起xsession(即先 export DISPLAY=:0.0,再 startx)。記得開啓 xhost +來使得允許連接到此x服務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章