• 作者:老汪软件技巧
  • 发表时间:2024-01-04 15:00
  • 浏览量:

一.简介

显示子系统是 平台显示输出相关软硬件系统的统称,它包括 VOP(比较老的平台叫 LCDC,比如 、)和 RGB、、BT656、I8080(MCU 显示接口),LVDS、MIPI DSI、EDP、DP、HDMI 等显示信号输 出模块以及与之对应的软件驱动。

RK3568 DRM显示框架

从上面的 DSS 框图可以看到,在整个显示通路的最后端,是由 RGA,GPU、VPU 组成的显示图形加速模块,他们是专 门针对图像处理优化设计的硬件 IP,能够高效的进行图像的生成和进一步处理(比如 GPU 通过 功能提供图像 渲染功能,RGA 可以对图像数据进行缩放,旋转,合成等 2D 处理,VPU 可以高效的进行视频解码),从而减轻 CPU

负担。 经过这些图像加速模块处理后的数据会存放在 DDR 中,然后由 VOP 读取,根据应用需求进行 Alpha 叠加,颜色空间 转换,gamma 矫正,HDR 转换 等处理后,再发送到对应的显示接口模块(HDMI/DP/DSI/RGB/LVDS), 这些接口模 块会把接收到的数据转换成符合各自协议的数据流,发送到显示器或者屏幕上,呈现在最终用户眼前。

二.DRM 驱动和 的交互过程

RK3568 DRM显示框架

三.DRM显示框架的DTS配置

在一颗 SOC 上,可能有多个 VOP,HDMI,eDP,DP,MIPI,Panel 模块,根据具体产品定义,一款产品可能只需要 使用到其中一部分模块来组成显示通路。具体使用哪些模块,以及这些模块之间如何衔接则通过 dts 配置。

display_subsystem: display-subsystem {
		compatible = "rockchip,display-subsystem";
		ports = <&vop_out>;
		route {
			route_dp0: route-dp0 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp1_out_dp0>;
			};
			route_dsi0: route-dsi0 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp3_out_dsi0>;
			};
			route_dsi1: route-dsi1 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp3_out_dsi1>;
			};
			route_edp0: route-edp0 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp2_out_edp0>;
			};
			route_edp1: route-edp1 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
			};
			route_hdmi0: route-hdmi0 {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp0_out_hdmi0>;
			};
			route_rgb: route-rgb {
				status = "disabled";
				logo,uboot = "logo.bmp";
				logo,kernel = "logo_kernel.bmp";
				logo,mode = "center";
				charge_logo,mode = "center";
				connect = <&vp3_out_rgb>;
			};
		};
	};

VOP:

vop: vop@fdd90000 {
		compatible = "rockchip,rk3588-vop";
		reg = <0x0 0xfdd90000 0x0 0x4200>, <0x0 0xfdd95000 0x0 0x1000>;
		reg-names = "regs", "gamma_lut";
		interrupts = ;
		clocks = <&cru ACLK_VOP>,
			 <&cru HCLK_VOP>,
			 <&cru DCLK_VOP0>,
			 <&cru DCLK_VOP1>,
			 <&cru DCLK_VOP2>,
			 <&cru DCLK_VOP3>,
			 <&cru PCLK_VOP_ROOT>,
			 <&cru DCLK_VOP0_SRC>,
			 <&cru DCLK_VOP1_SRC>,
			 <&cru DCLK_VOP2_SRC>;
		clock-names = "aclk_vop",
			      "hclk_vop",
			      "dclk_vp0",
			      "dclk_vp1",
			      "dclk_vp2",
			      "dclk_vp3",
			      "pclk_vop",
			      "dclk_src_vp0",
			      "dclk_src_vp1",
			      "dclk_src_vp2";
		assigned-clocks = <&cru ACLK_VOP>;
		assigned-clock-rates = <500000000>;
		resets = <&cru SRST_A_VOP>,
			 <&cru SRST_H_VOP>,
			 <&cru SRST_D_VOP0>,
			 <&cru SRST_D_VOP1>,
			 <&cru SRST_D_VOP2>,
			 <&cru SRST_D_VOP3>;
		reset-names = "axi",
			      "ahb",
			      "dclk_vp0",
			      "dclk_vp1",
			      "dclk_vp2",
			      "dclk_vp3";
		iommus = <&vop_mmu>;
		power-domains = <&power RK3588_PD_VOP>;
		rockchip,grf = <&sys_grf>;
		rockchip,vop-grf = <&vop_grf>;
		rockchip,vo1-grf = <&vo1_grf>;
		rockchip,pmu = <&pmu>;
		status = "disabled";
		vop_out: ports {
			#address-cells = <1>;
			#size-cells = <0>;
			vp0: port@0 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <0>;
				vp0_out_dp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dp0_in_vp0>;
				};
				vp0_out_edp0: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&edp0_in_vp0>;
				};
				vp0_out_hdmi0: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&hdmi0_in_vp0>;
				};
			};
			vp1: port@1 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <1>;
				vp1_out_dp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dp0_in_vp1>;
				};
				vp1_out_edp0: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&edp0_in_vp1>;
				};
				vp1_out_hdmi0: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&hdmi0_in_vp1>;
				};
			};
			vp2: port@2 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <2>;
				assigned-clocks = <&cru DCLK_VOP2_SRC>;
				assigned-clock-parents = <&cru PLL_V0PLL>;
				vp2_out_dp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dp0_in_vp2>;
				};
				vp2_out_edp0: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&edp0_in_vp2>;
				};
				vp2_out_hdmi0: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&hdmi0_in_vp2>;
				};
				vp2_out_dsi0: endpoint@3 {
					reg = <3>;
					remote-endpoint = <&dsi0_in_vp2>;
				};
				vp2_out_dsi1: endpoint@4 {
					reg = <4>;
					remote-endpoint = <&dsi1_in_vp2>;
				};
			};
			vp3: port@3 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <3>;
				vp3_out_dsi0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dsi0_in_vp3>;
				};
				vp3_out_dsi1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&dsi1_in_vp3>;
				};
				vp3_out_rgb: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&rgb_in_vp3>;
				};
			};
		};
	};

该节点描述 VOP 硬件资源,控制着 vop 驱动的加载 .c/.c , 它描述了如下 的显示通路连接关系:

RK3568 DRM显示框架

: ports 节点描述 VOP 的输出通道,vp0/1/2 对应 VOP 上 Video Port0/1/2 三个独立的输出通路。

vp0/1/2 : port 下的 节点描述 VP 和显示接口的连接关系,以上面的 dts 描述为例:vp0 节点下有

, , , 四个节点,说明 vp0 可以和 dsi0、dsi1、

edp、hdmi、四个显示接口连接。 每个 通过 - 属性和对应的显示接口组成一个连接通路 ,比如和 hdmi 显示接口的连接:

hdmi0: hdmi@fde80000 {
		compatible = "rockchip,rk3588-dw-hdmi";
		reg = <0x0 0xfde80000 0x0 0x20000>;
		interrupts = ,
			     ,
			     ,
			     ,
			     ;
		clocks = <&cru PCLK_HDMITX0>,
			 <&cru CLK_HDMIHDP0>,
			 <&cru CLK_HDMITX0_EARC>,
			 <&cru CLK_HDMITX0_REF>,
			 <&cru MCLK_I2S5_8CH_TX>,
			 <&cru DCLK_VOP0>,
			 <&cru DCLK_VOP1>,
			 <&cru DCLK_VOP2>,
			 <&cru DCLK_VOP3>,
			 <&hclk_vo1>,
			 <&hdptxphy_hdmi_clk0>;
		clock-names = "pclk",
			      "hpd",
			      "earc",
			      "hdmitx_ref",
			      "aud",
			      "dclk_vp0",
			      "dclk_vp1",
			      "dclk_vp2",
			      "dclk_vp3",
			      "hclk_vo1",
			      "link_clk";
		resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>;
		reset-names = "ref", "hdp";
		power-domains = <&power RK3588_PD_VO1>;
		pinctrl-names = "default";
		pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd &hdmim0_tx0_scl &hdmim0_tx0_sda>;
		reg-io-width = <4>;
		rockchip,grf = <&sys_grf>;
		rockchip,vo1_grf = <&vo1_grf>;
		phys = <&hdptxphy_hdmi0>;
		phy-names = "hdmi";
		#sound-dai-cells = <0>;
		status = "disabled";
		ports {
			#address-cells = <1>;
			#size-cells = <0>;
			hdmi0_in: port@0 {
				reg = <0>;
				#address-cells = <1>;
				#size-cells = <0>;
				hdmi0_in_vp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&vp0_out_hdmi0>;
					status = "disabled";
				};
				hdmi0_in_vp1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&vp1_out_hdmi0>;
					status = "disabled";
				};
				hdmi0_in_vp2: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&vp2_out_hdmi0>;
					status = "disabled";
				};
			};
		};
	};

结合上面的 dts 描述我们可以知道,在 上,hdmi 可以和 vop 的 vp0,vp1 连接。 需要注意的是,一个显示接口在同一个时刻只能和一个 vp 连接,所以在具体的板级配置中,需要在 dts 中把要使用的 通路打开,把不使用的通路设置为 状态。

&hdmi {
        status = "okay";
};
&hdmi_in_vp0 {
        status = "okay";
};
&hdmi_in_vp1 {
        status = "disabled";
};
&route_hdmi {
        status = "okay";
        connect = <&vp0_out_hdmi>;
};

四.调试手段

调试命令:

cat /sys/kernel/debug/dri/0/summary

RK3568 DRM显示框架

参数说明:

1. 两个红色方框表示两个显示设备使用的 vop 分别是 .vop 和 .vop;

2. 绿色部分表示 信息,两个显示设备分别为 eDP 屏和 MIPI 屏;

3. 粉色部分为显示模式,可以知道具体的时序、DCLK 以及帧率,上图中两个设备分别为分辨率为 的 eDP 屏和分辨率 的 MIPI 屏;

4. 蓝色部分是 VOP 图层信息,第一个显示设备打开 win0 图层,大小为 格式为 XRGB 第二个显示设备 打开 win0 图层,大小 格式为 XRGB, src 和 dst 表示源数据和显示的大小和位置,如果 src 和 dst 的 大小不一致,VOP 会进行缩放处理。

查看vop时钟命令:

cat /sys/kernel/debug/clk/clk_summary | grep vop