RK3568 设置VP0与VP1内容到对应显示接口(DSI0/DSI1)


前言

项目上结构上使用DSI1接口做单屏标签,而DSI1是VP1内容的输出,我们需要将VP0内容输出,设置到DSI1上。
为什么要把VP0设置到DSI1上?下面会有一个章节进行讲解。
这一章就是讲解RK3568如何把VP0设置到DSI1上,因为主要的修改都在设备树上,所以,本文仅围绕设备树进行讲解。
同时把下图的含义搞明白,对修改也具有一定的帮助。
下图是RK的说明
在这里插入图片描述
下图是DRM的标准说明
在这里插入图片描述
VPx的输出路径
在这里插入图片描述


一、为什么要把VP0设置到DSI1上?

我们已知VP0的内容可以输出到DSI1或DSI0上,但是VP0的内容是从哪来的?这就涉及到VPx与图层plane的关系,在RK中会有多个图层,主要有如下
rockchip_vop.h

#define	ROCKCHIP_VOP2_CLUSTER0	0
#define	ROCKCHIP_VOP2_CLUSTER1	1
#define	ROCKCHIP_VOP2_ESMART0	2
#define	ROCKCHIP_VOP2_ESMART1	3
#define	ROCKCHIP_VOP2_SMART0	4
#define	ROCKCHIP_VOP2_SMART1	5
#define	ROCKCHIP_VOP2_CLUSTER2	6
#define	ROCKCHIP_VOP2_CLUSTER3	7
#define	ROCKCHIP_VOP2_ESMART2	8
#define	ROCKCHIP_VOP2_ESMART3	9

已知有CLUSTER 0/1 等,这里分主图层与镜像图层,主图层是0,镜像图层是1,可知主图层一般用于不进行热插拔的接口(DSI),镜像图像用于可热插拔的接口。我们再从以下log来看,主图层在VPx中的分配
在这里插入图片描述
可知VP0使用了CLUSTER0、ESMART0、SMART0,即VP0使用的都是主图层,VP1使用的都是镜像图层,因此,如果,我们想要DSI1做主屏(不热插拔),那么我们需要把DSI1连接到VP0,DSI0连接到VP1。
注意:客户的需求就是一个app,单双屏都可以使用,单屏机器的结构上DSI1为主屏(DSI0不使用)

二、设备树显示分析

2.1 display_subsystem

	display_subsystem: display-subsystem {
		compatible = "rockchip,display-subsystem";
		...
		//显示内容的输出接口,这个后面会说明
		ports = <&vop_out>;
		...
		//路径
		route {
			route_dsi0: route-dsi0 {
				...
				//当前显示内容输出路径是vp0输出到dsi0(这部分内容可以从vop_out查看vp0所支持的显示接口)
				connect = <&vp0_out_dsi0>;
			};
			route_dsi1: route-dsi1 {
				...
				//当前显示内容输出路径是vp0输出到dsi1(这部分内容可以从vop_out查看vp0所支持的显示接口)
				connect = <&vp0_out_dsi1>;
			};
			...
		};
	};

由上所述,如果vp0做主屏内容输出,vp1做副屏内容输出,上述肯定是需要修改的。这里先做个标记,先不修改,先查看ports = <&vop_out>;的内容

2.2 vop_out

	vop: vop@fe040000 {
		compatible = "rockchip,rk3568-vop";
		reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
		reg-names = "regs", "gamma_lut";
		rockchip,grf = <&grf>;
		interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>, <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
		clock-names = "aclk_vop", "hclk_vop", "dclk_vp0", "dclk_vp1", "dclk_vp2";
		iommus = <&vop_mmu>;
		power-domains = <&power RK3568_PD_VO>;
		status = "disabled";
		//从这里分析
		vop_out: ports {
			#address-cells = <1>;
			#size-cells = <0>;
			//vp0,支持vp0_out_dsi0、vp0_out_dsi1、vp0_out_edp、vp0_out_hdmi
			//里面的remote-endpoint dsi0_in_vp0、dsi1_in_vp0 会有一个对应的端口,下面会进行讲解
			vp0: port@0 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <0>;

				vp0_out_dsi0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dsi0_in_vp0>;
				};

				vp0_out_dsi1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&dsi1_in_vp0>;
				};

				vp0_out_edp: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&edp_in_vp0>;
				};

				vp0_out_hdmi: endpoint@3 {
					reg = <3>;
					remote-endpoint = <&hdmi_in_vp0>;
				};
			};
			//vp1,支持vp1_out_dsi0、vp1_out_dsi1、vp1_out_edp、vp1_out_hdmi、vp1_out_lvds
			vp1: port@1 {
				#address-cells = <1>;
				#size-cells = <0>;
				reg = <1>;

				vp1_out_dsi0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&dsi0_in_vp1>;
				};

				vp1_out_dsi1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&dsi1_in_vp1>;
				};

				vp1_out_edp: endpoint@2 {
					reg = <2>;
					remote-endpoint = <&edp_in_vp1>;
				};

				vp1_out_hdmi: endpoint@3 {
					reg = <3>;
					remote-endpoint = <&hdmi_in_vp1>;
				};

				vp1_out_lvds: endpoint@4 {
					reg = <4>;
					remote-endpoint = <&lvds_in_vp1>;
				};
			};
			//vp2,支持vp1_out_dsi0、vp1_out_dsi1、vp1_out_edp、vp1_out_hdmi、vp1_out_lvds
			vp2: port@2 {
				#address-cells = <1>;
				#size-cells = <0>;

				reg = <2>;

				vp2_out_lvds: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&lvds_in_vp2>;
				};

				vp2_out_rgb: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&rgb_in_vp2>;
				};
			};
		};
	};

2.3 以dsi0、dsi1为例

	//这个是DSI0的显示接口
	dsi0: dsi@fe060000 {
		...
		ports {
			#address-cells = <1>;
			#size-cells = <0>;
			//dsi0可以接收的输出有dsi0_in_vp0、dsi0_in_vp1
			//其对应的remote-endpoint就是vp0_out_dsi0、vp1_out_dsi0
			dsi0_in: port@0 {
				reg = <0>;
				#address-cells = <1>;
				#size-cells = <0>;

				dsi0_in_vp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&vp0_out_dsi0>;
					status = "disabled";
				};

				dsi0_in_vp1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&vp1_out_dsi0>;
					status = "disabled";
				};
			};
		};
	};

	dsi1: dsi@fe070000 {
		...
		ports {
			#address-cells = <1>;
			#size-cells = <0>;
			//dsi1可以接收的输出有dsi1_in_vp0、dsi1_in_vp1
			//其对应的remote-endpoint就是vp0_out_dsi1、vp1_out_dsi1
			dsi1_in: port@0 {
				reg = <0>;
				#address-cells = <1>;
				#size-cells = <0>;

				dsi1_in_vp0: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&vp0_out_dsi1>;
					status = "disabled";
				};

				dsi1_in_vp1: endpoint@1 {
					reg = <1>;
					remote-endpoint = <&vp1_out_dsi1>;
					status = "disabled";
				};
			};
		};
	};

综合1.1、1.2、1.3,结合代码中一些端口的使能与关闭

2.4 VP0->DSI0、VP1->DSI1

DSI0

//DSI0的显示路径为vp0_out_dsi0
&route_dsi0 {
	status = "okay";
	connect = <&vp0_out_dsi0>;
};
//如果使用上述显示路径,那么需要做如下设置
//打开dsi0_in_vp0的使能
&dsi0_in_vp0 {
	status = "okay";
};
//关闭dsi0_in_vp1的使能
&dsi0_in_vp1 {
	status = "disabled";
};

由上述可知,dsi0的显示路径route_dsi0中:connect指定了vp0_out_dsi0,而dsi设备树中断ports也使能了dsi0_in_vp0,关闭了dsi0_in_vp1,即dsi0_in_vp0与vp0_out_dsi0一一对应。
DSI1

//DSI1的显示路径为vp1_out_dsi1
&route_dsi1 {
	status = "okay";
	connect = <&vp1_out_dsi1>;
};
//如果使用上述显示路径,那么需要做如下设置
//打开dsi1_in_vp1的使能
&dsi1_in_vp1{
	status = "okay";
};
//关闭dsi0_in_vp1的使能
&dsi1_in_vp0 {
	status = "disabled";
};

同上。
上述内容是把VP0内容输出到DSI0,VP1内容输出到DSI1,根据前言,我们需要把VP0内容输出到DSI1,VP1内容输出到DSI0,见第二章修改

三、修改VP1->DSI0、VP0->DSI1

DSI0

//DSI0的显示路径为vp1_out_dsi0,这个定义见第一节中的vop设备树节点内的定义
&route_dsi0 {
	status = "okay";
	connect = <&vp1_out_dsi0>;
};
//如果使用上述显示路径,那么需要做如下设置
//关闭dsi0_in_vp0的使能
&dsi0_in_vp0 {
	status = "disabled";
};
//打开dsi0_in_vp1的使能
&dsi0_in_vp1 {
	status = "okay";
};

DSI1

//DSI1的显示路径为vp0_out_dsi1
&route_dsi1 {
	status = "okay";
	connect = <&vp0_out_dsi1>;
};
//如果使用上述显示路径,那么需要做如下设置
//关闭dsi1_in_vp1的使能
&dsi1_in_vp1{
	status = "disabled";
};
//打开dsi0_in_vp1的使能
&dsi1_in_vp0 {
	status = "okay";
};

四、验证

4.1 基于2.4小节的验证

基于1.4小节的修改对DSI0、DSI1进行一个热插拔测试,有如下结果
对DSI0、DSI1进行拔出

echo off > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-1/status
echo off > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

先插入DSI1,DSI1是没有显示内容的(但如果先插入DSI0,DSI0是可以显示的,无需DSI1先插入)

echo on > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

需要把DSI0也插入,DSI1才会显示内容

echo on > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

综上所述,连接VP0的输出的DSI0是可以独立显示的,无需依赖DSI1
也可以从如下指令获取这些信息
在这里插入图片描述
可以看到VP0 的 connector是DSI-1(实际是DSI-0),使用的图层是主图层。
可以看到VP1 的 connector是DSI-2(实际是DSI-1),使用的图层是镜像图层。

4.2 基于第二章的修改验证

基于1.4小节的修改对DSI0、DSI1进行一个热插拔测试,有如下结果
对DSI0、DSI1进行拔出

echo off > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-1/status
echo off > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

先插入DSI1,DSI1是可以显示内容的

echo on > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

需要把DSI0也插入,DSI0也会显示内容(但是先插入DSI0(DSI1没有插入),DSI是没有显示内容的)

echo on > /sys/devices/platform/display-subsystem/drm/card0/card0-DSI-2/status

综上所述,连接VP0的输出的DSI1是可以独立显示的,无需依赖DSI0
从也可以dump这些信息出来
在这里插入图片描述

五、使用双屏异显APP的一些信息

5.1 基于2.4小节的验证

在这里插入图片描述

5.2 基于第二章的修改验证

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、主副内容显示

对上述进行修改后,我们仅仅是驱动中把主图层分配给了DSI1,镜像图层分配给DSI0,但是,主显示内容是否写入到主图层中,这个是由DRM应用程序决定的,需要看上层的实现,因此,驱动改完后,还需要上层对这些进行一个修改。DRM应用程序具体可以查看《DRM 应用程序简单理解》
主副屏显示内容的修改可以修改以下属性

vendor.hwc.device.primary
vendor.hwc.device.extend

还可以直接修改代码
hardware/rockchip/hwcomposer/drmhwc2/drm/drmdevice.cpp

  DrmConnector *primary = NULL;
  //新增这部分内容,把DSI2给找出来(如果不清楚这个属性,可以先把这个给打印出来,在写代码)
  for (auto &conn : connectors_) {
    //这里把主显示设置在DSI1(物理)上(这里是DSI2)
    const char* str1 = "DSI";
    if (strcmp(connector_type_str(conn->type()), str1) == 0) {
      ALOGE("zzh connect_type == DSI \n");
      if (conn->type_id() == 2) {
        ALOGE("zzh ++ connect id = %d \n", conn->type_id());
        found_primary = true;
        primary = conn.get();
        break;
      } else {
        ALOGE("zzh -- connect id = %d \n", conn->type_id());
      }
    } else {
      ALOGE("zzh connect_type != DSI \n");
    }
  }
  //下面这部分需要注释
  /*
  for (auto &conn : connectors_) {
    //ALOGE("zzh connect_type:%s, connect_id:%d \n", connector_type_str(conn->type()), conn->type_id());
    if (!(conn->possible_displays() & HWC_DISPLAY_PRIMARY_BIT))
      continue;
    if (conn->internal())
      continue;
    if (conn->state() != DRM_MODE_CONNECTED)
      continue;
    found_primary = true;
    if(NULL == primary){
      primary = conn.get();
    }else{
      // High priority devices can become the primary
      if(conn.get()->priority() < primary->priority()){
        primary = conn.get();
      }
    }
  }
  */

在这里插入图片描述
可以看到这个主显示内容与第五章的是不一样的。点击start activity,选择需要异显的应用,此时物理DSI0会显示异显应用(第五章全都是物理DSI1显示异显应用)

七、调转主副屏后,Recovery显示在副屏DSI0上

调转主副屏后,Recovery显示在副屏DSI0上,这个现象是我们不愿意看到的,
那么修改的方案就是要找到recovery的drm显示动画的代码。
bootable/recovery/minui/graphics_drm.cpp
然后connector需要选择DSI1,因此需要做如下修改

@@ -212,7 +212,7 @@ static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resourc
     drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
     if (connector) {
       if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
-          (connector->count_modes > 0)) {
+          (connector->count_modes > 0) && connector->connector_type_id == 2) {
		//主要是这个判断connector->connector_type_id == 2
		//==2时,说明connector是DSI1,此时,返回connector即可
		//==1时,connector应该是DS0,这个就是之前默认的
         return connector;
       }
       drmModeFreeConnector(connector);
<think>我们正在处理一个关于RK3588芯片的VP3接口配置问题,特别是针对BT1120视频输入。BT1120是一种并行数字视频接口标准,常用于专业视频设备。根据引用内容,我们知道RK3588的视频处理单元(VPU)有多个视频端口(VP0-VP3),并且每个端口有特定的限制。 关键信息回顾: -引用[1]提到:hdmi,edp, dp只能显示VP0VP1VP2上;dsi0dsi1只能显示VP2或VP3上;rgb只能显示VP3上。 -由于BT1120通常使用并行接口,类似于RGB或YUV并行输入,因此我们可以推断BT1120可能会使用到VP3接口,因为RGB只能显示VP3上,而BT1120也是一种并行接口。因此,配置VP3以支持BT1120输入需要关注以下几个方面:1.硬件连接:确保BT1120信号源正确连接到RK3588的相应引脚。2.设备树(Device Tree)配置:在Linux内核中,需要通过设备树来配置VP3接口,使其支持BT1120输入。3.驱动程序:确保内核中包含了支持BT1120输入的驱动程序,并且VP3接口匹配。步骤:1.**硬件连接**:检查开发板的原理图,确认BT1120信号线连接到RK3588的哪个引脚,并确保这些引脚被配置为视频输入模式。 2. **设备树配置**:在设备树源文件(dts)中,我们需要配置VP3节点,指定其作为视频输入端口,并设置相关参数(如数据宽度、同步信号极性等)以匹配BT1120标准。 由于引用中没有直接提供BT1120的配置示例,我们可以参考RK3588的技术参考手册(TRM)和Linux内核中的设备树绑定文档。以下是一个推测的设备树配置示例: ```dts&vp3 {status= "okay";//可能需要配置为输入模式,并设置输入源};//配置BT1120接口bt1120_in:bt1120-in {compatible= "some,bt1120";//需要根据实际驱动确定//其他参数,如数据位宽、同步信号等port {bt1120_in_vp3: endpoint{remote-endpoint =<&vp3_in_bt1120>;};};}; //将VP3的输入源设置为BT1120&vp3_in{vp3_in_bt1120: endpoint{remote-endpoint =<&bt1120_in>;};}; ```3. **驱动支持**:确保内核配置了BT1120输入驱动和RK3588的视频端口驱动。可能需要在内核中启用相关模块。注意事项: -由于RK3588的VP3通常用于输出(如RGB),我们需要确认它是否可以被配置为输入。根据引用[1]中的描述,VP3用于RGB输出,但未明确说明是否支持输入。因此,需要查阅技术手册确认VP3是否支持视频输入功能。 -如果VP3不支持输入,那么可能需要使用其他视频端口(如VP0VP1VP2),但引用中明确提到RGB只能显示VP3上,而BT1120是类似RGB的并行接口,所以VP3可能是唯一选择。但要注意,BT1120输入可能需要特定的接口(如DVP或BT656/BT1120接口),而RK3588可能有专门的视频输入接口(如MIPICSI或HDMI输入),但BT1120通常通过并行接口输入。 因此,如果RK3588的VP3不支持输入,那么可能需要使用其他接口模块(如通过MIPI CSI转接芯片)或者确认是否有其他视频输入端口支持BT1120。根据以上分析,建议步骤: 1.查阅RK3588的技术参考手册,确认VP3是否支持视频输入模式,以及如何配置。 2.参考Linux内核中Rockchip平台的相关设备树配置,特别是视频输入部分。 3.如果官方文档或示例不足,可以查看类似平台(如RK3399)的BT1120配置作为参考。由于我们没有具体的RK3588手册,这里只能提供一般性指导。**生成相关问题**: 1. RK3588的VP3接口是否支持视频输入模式? 2.如何配置RK3588的设备树以支持BT1120视频输入? 3. RK3588的BT1120输入驱动需要哪些内核配置? 4.在飞凌OK3588-C开发板上,BT1120接口的硬件连接如何?5.如果VP3不支持输入,RK3588是否有其他接口可以支持BT1120输入?</think>### RK3588 VP3接口配置BT1120视频输入指南 在RK3588平台上配置VP3接口支持BT1120视频输入,需要结合硬件连接和软件配置实现。以下是关键步骤和注意事项: #### 1. **硬件连接要求** - **引脚映射**:BT1120使用16/20位并行接口(YUV 4:2:2),需将数据线连接到RK3588的VP3接口引脚。根据RK3588技术手册: - VP3数据引脚对应`GPIO1_D0-D7`(低8位)和`GPIO2_A0-A7`(高8位) - 同步信号:HSYNC接`GPIO2_B0`, VSYNC接`GPIO2_B1`, PCLK接`GPIO2_B2` - **电平匹配**:BT1120采用3.3V LVCMOS电平,需确保信号源电平匹配 - **物理接口**:建议通过FPC连接器接入开发板的RGB接口VP3专用) #### 2. **设备树配置** 在设备树文件(如`rk3588s-ok3588-c.dts`)中添加VP3的BT1120输入节点: ```dts &vp3 { status = "okay"; ports { vp3_in: port { #address-cells = <1>; #size-cells = <0>; // 配置BT1120输入源 bt1120_in_vp3: endpoint@0 { reg = <0>; remote-endpoint = <&bt1120_out>; // BT1120参数:1080P@30fps bus-width = <16>; hsync-active = <1>; vsync-active = <1>; pclk-sample = <1>; }; }; }; }; // BT1120解码器节点 bt1120: bt1120@fdee0000 { compatible = "rockchip,rk3588-bt1120"; reg = <0x0 0xfdee0000 0x0 0x10000>; interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru ACLK_BT1120>; clock-names = "aclk"; port { bt1120_out: endpoint { remote-endpoint = <&bt1120_in_vp3>; }; }; }; ``` #### 3. **内核驱动配置** ```bash # 在内核配置中启用驱动 CONFIG_VIDEO_ROCKCHIP_BT1120=y CONFIG_VIDEO_ROCKCHIP_VP3=y # 加载模块 insmod rockchip_bt1120.ko insmod rockchip_vp3.ko ``` #### 4. **验证配置** - **信号检测**: ```bash # 查看视频节点 v4l2-ctl --list-devices # 预期输出:/dev/video20 - BT1120-in (vp3) ``` - **数据捕获**: ```bash # 使用GStreamer捕获视频 gst-launch-1.0 v4l2src device=/dev/video20 ! videoconvert ! xvimagesink ``` #### 5. **关键限制注意事项** 1. **接口独占性**: VP3RGB接口共享物理引脚[^1],启用BT1120输入时不可同时使用RGB输出。 2. **时钟域约束**: BT1120的PCLK需满足: $$ 10\text{MHz} \leq \text{PCLK} \leq 150\text{MHz} $$ 建议在设备树中配置`assigned-clocks`精确匹配输入源时钟。 3. **内存带宽优化**: 高分辨率输入时需在`dma-controller`节点增加带宽分配: ```dts &video_phy { rockchip,bw-ratio = /bits/ 16 <500>; }; ``` 4. **同步信号极性**: 若出现图像撕裂,调整设备树的`hsync-active`/`vsync-active`值(01)。 --- ### 常见问题解决 | 现象 | 可能原因 | 解决方案 | |------|----------|----------| | 无视频信号 | 引脚配置错误 | 检查`pinctrl`映射关系 | | 图像错位 | 同步极性不匹配 | 调整`hsync-active/vsync-active` | | 帧率不稳定 | 内存带宽不足 | 增加`bw-ratio`值或降低分辨率 | | 驱动加载失败 | 时钟未启用 | 确认ACLK_BT1120时钟树配置 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值