eDP Bridge on Iris 2.0 + imx8x

I have Iris 2.0 + imx8x and have LVDS display 800x480pix connected to (X7) on Iris

I need display port :frowning:

my idea:

will this work?
Will the image be the same on LVDS and DP? (I need clone screen)
How to enable MIPI_DSI0 in device tree ?

First step
I found driver sn65dsi86

CONFIG_DRM_TI_SN65DSI86=m

now I’m looking for how to use it in the device tree :wink:

Hi @MariusM,

Please check Documentation/devicetree/bindings/display/bridge/ti,sn65dsi86.yaml, it will describe the configurations and also it has some examples at the bottom. This one is for kernel toradex_5.15-2.2.x-imx.

Regards,
Hiago.

Thank you @hfranco.tx for the materials.
There is no one in my organization who knows Device-tree.
Do you have any educational materials to learn this?
or
Can such a problem be outsourced to a company?

Hi @MariusM ,

we have quite extensive documentation about Device Tree: Device Tree Technical Overview | Toradex Developer Center.

We have an example of the Device Tree Overlay for our Verdin DSI to LVDS Adapter.

I can give you a rough overview of the process:

  1. Understand the block diagram and how your hardware is connected. In our case, this adapter can connect to our Dahlia Board via X1 connector. This will interface with our evaluation display Capacitive Touch Display 10.1" LVDS via X2 connector for the pixel data and X3 connector for the capacitive touch functionality, which use I2C interface/communication protocol.

  2. Since this adapter is physically separate from the carrier board, we use the concept of Device Tree Overlay. As an example, if we want to enable the LVDS display (1) using the adapter (2) for the Verdin iMX8M Mini module (3), the overlay looks like:

/*this describes the bridge hardware for the kernel; corresponds to (2) above 
source: https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/display-lt170410_sn65dsi84_overlay.dtsi?h=toradex_5.15-2.2.x-imx */
#include "verdin-imx8_mipi-dsi-to-sn65dsi84.dtsi"

/*this describes the display for the kernel; corresponds to (1) above
source: https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/display-lt170410_sn65dsi84_overlay.dtsi?h=toradex_5.15-2.2.x-imx */
#include "display-lt170410_sn65dsi84_overlay.dtsi"

This is also a good resource to go: Setting up Displays with Torizon | Toradex Developer Center and this is a recording of one webinar Demystifying Device Tree for NXP i.MX Processors.

If you have further questions, feel free to ask. If you feel the task is too much, I suggest you to contact your account manager to discuss about the possibility to outsource the work.

1 Like

Hello,

Additionally to @andi.tx answer, there are some examples of this node being used in the Linux kernel by other devices:

~/Projects/linux master
❯ rg "ti,sn65dsi86" arch/arm64/boot/dts
arch/arm64/boot/dts/freescale/imx8mq-mnt-reform2.dts
141:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/renesas/r8a779a0-falcon-cpu.dtsi
205:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/renesas/r8a779g0-white-hawk-cpu.dtsi
230:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts
345:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
423:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi
658:            compatible = "ti,sn65dsi86";

arch/arm64/boot/dts/qcom/sc7180-trogdor-ti-sn65dsi86.dtsi
35:             compatible = "ti,sn65dsi86";

For example the first one is at linux/arch/arm64/boot/dts/freescale/imx8mq-mnt-reform2.dts at master · torvalds/linux · GitHub.

This could also help you to write the device tree node. Please note that I shared the master branch, which might differ from the branch you are currently working on, so some configurations might or might not be different.

Regards,
Hiago.

1 Like

Thank you very much for your professional answer @hfranco.tx @andi.tx .
I analyze the submitted materials

Thank you for the materials @hfranco.tx @andi.tx !
Now I understand the syntax of the language.
Now I need to understand how to connect the blocks

In imx8x-colibri.dtsi I found MIPI DSI converter to HDMI

/* MIPI DSI accessible on FFC (X2) */
&i2c0_mipi_lvds0 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_i2c0_mipi_lvds0>;
        clock-frequency = <100000>;
        status = "disabled";

        #address-cells = <1>;
        #size-cells = <0>;

        /* DSI to HDMI Adapter V1.1A */
        pca9540_switch: i2c-switch@70 {
                compatible = "nxp,pca9540";
                reg = <0x70>;
                i2c-mux-idle-disconnect;
                status = "disabled";

                #address-cells = <1>;
                #size-cells = <0>;

                /* DDC/EDID */
                i2c_sw0: i2c-sw@0 {
                        reg = <0>;
                };

                /* DSI-HDMI converter */
                i2c-sw@1 {
                        reg = <1>;

                        #address-cells = <1>;
                        #size-cells = <0>;

                        lt8912_hdmi: dsihdmi@48 {
                                compatible = "lontium,lt8912b";
                                reg = <0x48>;

                                ports {
                                        #address-cells = <1>;
                                        #size-cells = <0>;

                                        port@0 {
                                                reg = <0>;

                                                lt8912_in: endpoint {
                                                        data-lanes = <1 2 3 4>;
                                                        remote-endpoint = <&mipi0_dsi_host_out>;
                                                };
                                        };

                                        port@1 {
                                                reg = <1>;

                                                lt8912_out: endpoint {
                                                        remote-endpoint = <&hdmi_connector_in>;
                                                };
                                        };
                                };
                        };
                };
        };
};

I must put my device on i2c0_mipi_lvds0 branch
or I must use mipi0_dsi_host or mipi0_dphy ?

On (X2) is MIPI/DSI and LVDS , I do not know what to choose

hi @MariusM ,
which display will you use?

As I mentioned before, it is important to understand the underlying hardware connection since device tree is a way to describe/“introduce” your hardware to the kernel so that it can get the correct driver and use the hardware correctly.

I don’t have experience with eDP display yet, but we can take the example from MNT Reform 2 given by @hfranco.tx as it is a grea example of open source hardware with good documentation: MNT Reform Operator Handbook — MNT Reform Operator Handbook documentation. They successfully tested some eDP displays too: Parts — MNT Reform Operator Handbook documentation.

Now we take a step back and look at the block diagram of the hardware. This is taken from TI SN65DSI86 datasheet:

Now, comparing this with your board then we identify:

  1. Application Processor: we target Colibri iMX8X + Iris
  2. The bridge itself
  3. Panel: your intended display

Each of the hardware components should be described in the device tree according to the “binding” documentation (the link points to toradex_5.15-2.2.x-imx branch for our BSP 6). Some useful bindings to understand how to write the device tree:

  1. ti,sn65dsi86.yaml « bridge « display « bindings « devicetree « Documentation mentioned by @hfranco.tx previously
  2. Generic binding for display panel: panel-common.yaml « panel « display « bindings « devicetree « Documentation
  3. Example of an eDP display: innolux,p120zdg-bf1.yaml « panel « display « bindings « devicetree « Documentation

Take your time to read and understand them. The bindings usually give example and minimum required properties to be described in the device tree. This is in general important to work with device tree.

For the component 1 above (Colibri iMX8X + Iris), there is no binding, but obviously you can use the.dts directly. And refer to the diagram given here. It is very useful to understand our board definition and the blocks are clickable directly to the source code.

Now we try to compare this with the system diagram from MNT Reform 2 and we focus on the display part:

.

We see in this example that the display is connected to the bridge and also PWM for the backlight from the SoC and when we study the device tree of the MNT Reform 2 (and the bindings before), we see some important nodes accordingly:

/ {
	...
	/* you also find this node in https://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi?h=toradex_5.15-2.2.x-imx */ 
	backlight: backlight {
		compatible = "pwm-backlight";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_backlight>;
		pwms = <&pwm2 0 10000 0>;
		power-supply = <&reg_main_usb>;
		enable-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
		brightness-levels = <0 32 64 128 160 200 255>;
		default-brightness-level = <6>;
	};

	/* compare the following with example from https://git.toradex.com/cgit/linux-toradex.git/tree/Documentation/devicetree/bindings/display/panel/innolux,p120zdg-bf1.yaml?h=toradex_5.15-2.2.x-imx)*/
	panel {
		compatible = "innolux,n125hce-gn1";
		power-supply = <&reg_main_3v3>;
		backlight = <&backlight>;
		no-hpd;

		port {
			panel_in: endpoint {
				remote-endpoint = <&edp_bridge_out>;
			};
		};
	};

	reg_main_3v3: regulator-main-3v3 {
		compatible = "regulator-fixed";
		regulator-name = "3V3";
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;
	};

	reg_main_usb: regulator-main-usb {
		compatible = "regulator-fixed";
		regulator-name = "USB_PWR";
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
		vin-supply = <&reg_main_5v>;
	};

	reg_main_1v8: regulator-main-1v8 {
		compatible = "regulator-fixed";
		regulator-name = "1V8";
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <1800000>;
		vin-supply = <&reg_main_3v3>;
	};

	reg_main_1v2: regulator-main-1v2 {
		compatible = "regulator-fixed";
		regulator-name = "1V2";
		regulator-min-microvolt = <1200000>;
		regulator-max-microvolt = <1200000>;
		vin-supply = <&reg_main_5v>;
	};

	sound {
		compatible = "fsl,imx-audio-wm8960";
		audio-cpu = <&sai2>;
		audio-codec = <&wm8960>;
		audio-routing =
			"Headphone Jack", "HP_L",
			"Headphone Jack", "HP_R",
			"Ext Spk", "SPK_LP",
			"Ext Spk", "SPK_LN",
			"Ext Spk", "SPK_RP",
			"Ext Spk", "SPK_RN",
			"LINPUT1", "Mic Jack",
			"Mic Jack", "MICB",
			"LINPUT2", "Line In Jack",
			"RINPUT2", "Line In Jack";
		model = "wm8960-audio";
	};
};

/* this seems also to be important if your display has speaker and you want to enable it */
&i2c3 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c3>;
	status = "okay";

	wm8960: codec@1a {
		compatible = "wlf,wm8960";
		reg = <0x1a>;
		clocks = <&clk IMX8MQ_CLK_SAI2_ROOT>;
		clock-names = "mclk";
		#sound-dai-cells = <0>;
	};

	rtc@68 {
		compatible = "nxp,pcf8523";
		reg = <0x68>;
	};
};

/* I would say this is the most important node between the SoC and the bridge. Pay close attention to all labels in the phandle too (everything that starts with &) */
&i2c4 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c4>;
	clock-frequency = <400000>;
	status = "okay";

	edp_bridge: bridge@2c {
		compatible = "ti,sn65dsi86";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_edp_bridge>;
		reg = <0x2c>;
		enable-gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>;
		vccio-supply = <&reg_main_1v8>;
		vpll-supply = <&reg_main_1v8>;
		vcca-supply = <&reg_main_1v2>;
		vcc-supply = <&reg_main_1v2>;

		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;

				edp_bridge_in: endpoint {
					remote-endpoint = <&mipi_dsi_out>;
				};
			};

			port@1 {
				reg = <1>;

				edp_bridge_out: endpoint {
					remote-endpoint = <&panel_in>;
				};
			};
		};
	};
};

&mipi_dsi {
	status = "okay";

	ports {
		port@1 {
			reg = <1>;

			mipi_dsi_out: endpoint {
				remote-endpoint = <&edp_bridge_in>;
			};
		};
	};
};

&pwm2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm2>;
	status = "okay";
};

&sai2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_sai2>;
	assigned-clocks = <&clk IMX8MQ_CLK_SAI2>;
	assigned-clock-parents = <&clk IMX8MQ_CLK_25M>;
	assigned-clock-rates = <25000000>;
	fsl,sai-mclk-direction-output;
	fsl,sai-asynchronous;
	status = "okay";
};

&iomuxc {
	pinctrl_backlight: backlightgrp {
		fsl,pins = <
			MX8MQ_IOMUXC_GPIO1_IO10_GPIO1_IO10		0x3
		>;
	};

	pinctrl_edp_bridge: edpbridgegrp {
		fsl,pins = <
			MX8MQ_IOMUXC_SAI5_RXC_GPIO3_IO20		0x1
		>;
	};

	pinctrl_i2c4: i2c4grp {
		fsl,pins = <
			MX8MQ_IOMUXC_I2C4_SCL_I2C4_SCL			0x40000022
			MX8MQ_IOMUXC_I2C4_SDA_I2C4_SDA			0x40000022
		>;
	};

	pinctrl_pwm2: pwm2grp {
		fsl,pins = <
			MX8MQ_IOMUXC_SPDIF_RX_PWM2_OUT			0x3
		>;
	};

	pinctrl_sai2: sai2grp {
		fsl,pins = <
			MX8MQ_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0		0xd6
			MX8MQ_IOMUXC_SAI2_RXFS_SAI2_RX_SYNC		0xd6
			MX8MQ_IOMUXC_SAI2_TXC_SAI2_TX_BCLK		0xd6
			MX8MQ_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC		0xd6
			MX8MQ_IOMUXC_SAI2_RXC_SAI2_RX_BCLK		0xd6
			MX8MQ_IOMUXC_SAI2_MCLK_SAI2_MCLK			0xd6
			MX8MQ_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0		0xd6
		>;
	};

};

After studying, now it’s time for work. So, as an overview to get started:

  1. Use the example above as templates - keep those nodes and labels in mind because we want to implement them
  2. Write your own .dts (e.g. my-colibri-imx8x-iris-edp.dts based on imx8qxp-colibri-iris-v2.dts or imx8dx-colibri-iris-v2.dts) for your carrier board and create such nodes if it is not available
  3. Adapt the labels and property values according to your specification/our module specification (our device tree)

Don’t change any of our .dtsi as this can change when we update our board in the future. Keep all changes in your own .dts.

I don’t have eDP display with the bridge to test but I think that’s all about what I learned through that MNT Reform 2 example.

Of course, this is not meant to be a comprehensive step-by-step how-to, but I hope this gives a better idea for you. You will still need to invest some time and do many trial-and-errors (grab coffee or take a walk as always if you are frustrated :slight_smile: ).

It’s your turn now to let us know how it works and share your findings. Let us know if you have issues.