RK3588 HDMIIN调试(HDMI转MIPI-CSI)

这篇文件介绍一下RK3588平台HDMI转MIPI-CSI的调试与配置步骤。在前面的文章有介绍了RK356X HDMIIN的调试,这篇文章在介绍一下在RK3588平台的调试。RK3588除了自带有HDMIIN,还可以通过转接芯片,将HDMI抓成MIPI-CSI信号,作为类camera设备进行接收处理。

目录

(1)HDMI转MIPICSI常用转接芯片介绍

1.LT6911UXC/LT6911UXE规格

2.IT6616

3.RK628D

(2)转接芯片驱动调试

1.驱动相关接口配置

①lt6911uxe_get_detected_timings

②lt6911uxe_query_dv_timings

③lt6911uxe_g_mbus_config

④lt6911uxe_get_fmt

⑤分辨率变化事件

⑥拔插事件上报

2.dts配置

(3)安卓HAL适配

(4)总结


(1)HDMI转MIPICSI常用转接芯片介绍

之前的文章有介绍到RK3588 MIPI-CSI的规格,共有4个4lane的MIPI接口,DPHY速率可以达到2.5Gbps,总带宽就可以达到10Gbps,意味着可以支持到4K60 YUV422的输入。能否支持HDMI 4K60主要取决于转接芯片的MIPI TX DPHY的规格。这边介绍一下常见的几款转接芯片。

1.LT6911UXC/LT6911UXE规格

LT6911UXC和LT6911UXE是龙讯设计的支持HDMI转MIPI-CSI的桥接芯片。

LT6911UXC:MIPI TX DPHY的规格为DPHY1.2版本,最高速率2.0Gbps,单个MIPI PORT(4lane)没办法支持4K60输出,只能达到4K30的规格。在两个MIPI PORT 一共8lane的场景下可以输出4K60(似乎RK3588也已经支持这种方式)。

LT6911UXE:MIPI TX DPHY规格为DPHY1.2版本,最高速率2.5Gbps,单个MIPI PORT(4lane)支持4K60输出,适配RK3588可以省下主控的一个MIPI DPHY口。

2.IT6616

IT6616是ITE设计的HDMI转MIPI-CSI桥接芯片,DPHY速率最高2.0Gbps,支持HDMI1.4,可以支持4K30 YUV422MIPI输出,无法支持4K60 YUV422。

3.RK628D

RK628D是RK自己设计的一款桥接芯片,支持HDMI1.4,MIPI TX DPHY速率是1.2Gbps,支持MIPI TX输出YUV422 4K30。

(2)转接芯片驱动调试

HDMI转MIPI-CSI转接芯片可以当做类camera的设备来处理,基于V4L2框架实现,对接到RK3588的VICAP控制器。这边以LT6911UXE为例。

1.驱动相关接口配置

驱动的相关接口与camera大体相同,主要接口配置如下:

①lt6911uxe_get_detected_timings

获取转接芯片识别到的输入源的timing,一般是中断函数会调用到该函数。

②lt6911uxe_query_dv_timings

查询对应的timing,提供给上层应用查询分辨率等信息。

static int lt6911uxe_query_dv_timings(struct v4l2_subdev *sd,
				struct v4l2_dv_timings *timings)
{
	struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd);

	*timings = lt6911uxe->timings;
	if (debug)
		v4l2_print_dv_timings(sd->name,
				"query_dv_timings: ", timings, false);

	if (!v4l2_valid_dv_timings(timings, &lt6911uxe_timings_cap, NULL,
				NULL)) {
		v4l2_dbg(1, debug, sd, "%s: timings out of range\n",
				__func__);

		return -ERANGE;
	}

	return 0;
}

③lt6911uxe_g_mbus_config

总线相关信息的设置,lane数,通道数等等。

static int lt6911uxe_g_mbus_config(struct v4l2_subdev *sd,
			unsigned int pad, struct v4l2_mbus_config *cfg)
{
	struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd);
	u32 lane_num = lt6911uxe->bus_cfg.bus.mipi_csi2.num_data_lanes;
	u32 val = 0;

	val = 1 << (lane_num - 1) |
		V4L2_MBUS_CSI2_CHANNEL_0 |
		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;

	cfg->type = lt6911uxe->bus_cfg.bus_type;
	cfg->flags = val;

	return 0;
}

④lt6911uxe_get_fmt

获取相关的分辨率信息等。

static int lt6911uxe_get_fmt(struct v4l2_subdev *sd,
			struct v4l2_subdev_pad_config *cfg,
			struct v4l2_subdev_format *format)
{
	struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd);
	const struct lt6911uxe_mode *mode;

	mutex_lock(&lt6911uxe->confctl_mutex);
	format->format.code = lt6911uxe->mbus_fmt_code;
	format->format.width = lt6911uxe->timings.bt.width;
	format->format.height = lt6911uxe->timings.bt.height;
	format->format.field =
		lt6911uxe->timings.bt.interlaced ?
		V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
	format->format.colorspace = V4L2_COLORSPACE_SRGB;
	mutex_unlock(&lt6911uxe->confctl_mutex);

	mode = lt6911uxe_find_best_fit(lt6911uxe);
	lt6911uxe->cur_mode = mode;

	__v4l2_ctrl_s_ctrl_int64(lt6911uxe->pixel_rate,
				LT6911UXE_PIXEL_RATE);
	__v4l2_ctrl_s_ctrl(lt6911uxe->link_freq,
				mode->mipi_freq_idx);

	v4l2_dbg(1, debug, sd, "%s: mode->mipi_freq_idx(%d)", __func__, mode->mipi_freq_idx);

	v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field code:%d\n",
			__func__, format->format.code, format->format.width,
			format->format.height, format->format.field);

	return 0;
}

⑤分辨率变化事件

HDMI转MIPI-CSI类型的驱动与普通的camera驱动,最大的区别在于驱动需要检测输入源分辨率的变化与拔插的变化,以事件的形式上报,应用受到事件之后,需要重新以新的分辨率下发预览。

事件订阅:分别注册分辨率变化和拔插的事件。

static int lt6911uxe_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
				    struct v4l2_event_subscription *sub)
{
	switch (sub->type) {
	case V4L2_EVENT_SOURCE_CHANGE:
		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
	case V4L2_EVENT_CTRL:
		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
	default:
		return -EINVAL;
	}
}

分辨率变化事件一般在分辨率变化的中断中实现上报,v4l2_subdev_notify_event接口上报事件。

static void lt6911uxe_format_change(struct v4l2_subdev *sd)
{
	struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd);
	struct v4l2_dv_timings timings;
	const struct v4l2_event lt6911uxe_ev_fmt = {
		.type = V4L2_EVENT_SOURCE_CHANGE,
		.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
	};

	if (lt6911uxe_get_detected_timings(sd, &timings)) {
		enable_stream(sd, false);
		v4l2_dbg(1, debug, sd, "%s: No signal\n", __func__);
	}

	if (!v4l2_match_dv_timings(&lt6911uxe->timings, &timings, 0, false)) {
		enable_stream(sd, false);
		/* automatically set timing rather than set by user */
		lt6911uxe_s_dv_timings(sd, &timings);
		v4l2_print_dv_timings(sd->name,
				"Format_change: New format: ",
				&timings, false);
		if (sd->devnode && !lt6911uxe->i2c_client->irq)
			v4l2_subdev_notify_event(sd, &lt6911uxe_ev_fmt);
	}
	if (sd->devnode && lt6911uxe->i2c_client->irq)
		v4l2_subdev_notify_event(sd, &lt6911uxe_ev_fmt);
}

⑥拔插事件上报

拔插事件通过如下接口设置,当变量发生变化的时候,会上报事件,一般驱动需要实现拔插的检测,目前的做法是将HDMI的5V与RK3588的io相连接,使用io状态来检测拔插的动作。

static int lt6911uxe_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd)
{
	struct lt6911uxe *lt6911uxe = to_lt6911uxe(sd);

	return v4l2_ctrl_s_ctrl(lt6911uxe->detect_tx_5v_ctrl,
			tx_5v_power_present(sd));
}

2.dts配置

关键dts链路l配置如下:lt6911uxe-> csi2_dphy -> mipi2_csi2 -> rkcif_mipi_lvds2。

&csi2_dphy0 {
	status = "okay";

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

			hdmi_mipi2_in: endpoint@1 {
				reg = <1>;
				remote-endpoint = <&lt6911uxe_out1>;
				data-lanes = <1 2 3 4>;
			};
		};
		port@1 {
			reg = <1>;
			#address-cells = <1>;
			#size-cells = <0>;

			csidphy0_out: endpoint@0 {
				reg = <0>;
				remote-endpoint = <&mipi2_csi2_input>;
			};
		};
	};
};

&csi2_dphy0_hw {
	status = "okay";
};

&i2c3 {
	status = "okay";

	lt6911uxe_1: lt6911uxe_1@2b {
		compatible = "lontium,lt6911uxe";
		status = "okay";
		reg = <0x2b>;
		clocks = <&ext_cam_clk>;
		clock-names = "xvclk";
		power-domains = <&power RK3588_PD_VI>;
		pinctrl-names = "default";
		pinctrl-0 = <&lt6911uxe_pin_1>;
		interrupt-parent = <&gpio1>;
		interrupts = <RK_PB3 IRQ_TYPE_LEVEL_LOW>;
		// reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>;
		// power-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>;
		plugin-det-gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>;
		rockchip,camera-module-index = <0>;
		rockchip,camera-module-facing = "back";
		rockchip,camera-module-name = "HDMI-MIPI2";
		rockchip,camera-module-lens-name = "LT6911UXE-2";
		port {
			lt6911uxe_out1: endpoint {
				remote-endpoint = <&hdmi_mipi2_in>;
				data-lanes = <1 2 3 4>;
			};
		};
	};
};

&mipi2_csi2 {
	status = "okay";

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

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

			mipi2_csi2_input: endpoint@1 {
				reg = <1>;
				remote-endpoint = <&csidphy0_out>;
			};
		};

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

			mipi2_csi2_output: endpoint@0 {
				reg = <0>;
				remote-endpoint = <&cif_mipi_in2>;
			};
		};
	};
};

&rkcif {
	status = "okay";
};

&rkcif_mipi_lvds2 {
	status = "okay";

	port {
		cif_mipi_in2: endpoint {
			remote-endpoint = <&mipi2_csi2_output>;
		};
	};
};

&rkcif_mmu {
	status = "okay";
};

&pinctrl {
	hdmiin {

		lt6911uxe_pin_1: lt6911uxe-pin-1 {
			rockchip,pins = <1 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>,
					<1 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

(3)安卓HAL适配

cameraHAL 的适配需要在camera3_profiles.xml中添加相关的配置。

name需要与驱动对应的名字匹配。moduleid需要与dts配置的index相匹配。

需要支持的分辨率与帧率添加:

 配置成SOC模式,不需要经过ISP进行效果的调试。

(4)总结

在RK3588平台上,HDMI转MIPI CSI调试与camera调试大同小异,驱动无非就是针对HDMI源分辨率可能发生变化或者发生拔插来进行针对性的添加事件上报机制,然后应用去处理对应的事件。

猜你喜欢

转载自blog.csdn.net/qq_34341546/article/details/129009091