LVDS屏

MIPI DTS配置

Neardi-3568的SDK有MIPI DSI的DTS文件: rk3568-neardi-linux-ld120-mipi2lvds-EV101WXM-N10.dtsi,此 DTS 文件为 MIPI_EV101WXM-N10 10.1寸屏配置。

VP 分配

从DTS文件中我们可以看到以下语句:

&video_phy1 {
	status = "okay";
};

&dsi1_in_vp0 {
	status = "okay";
};

&dsi1_in_vp1 {
	status = "disabled";
};

&route_dsi1 {
	status = "okay";
	connect = <&vp0_out_dsi1>;
};

vp 是视频接口的意思,这一段表示 DSI1 开启并使用 vp1,HDMI 开启并使用 vp0 DSI1 和 HDMI 交换 vp 也是可以的,要保证不同的屏幕需要使用不同的 vp

引脚配置

&dsi1 {
	status = "okay";
	rockchip,lane-rate = <854>;
	enable-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;	// lvds_pwren
	reset-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;		// lvds_rst
	......

&pinctrl {

	 backlight {
               /* for edp and dsi2lvds */
               lcd_en: lcd-en {
                       rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>;
               };
       };
};

这里定义了LCD的控制引脚:

NAME GPIO GPIO_ACTIVE
enable-gpios(lvds_pwren) RK_PC7 GPIO_ACTIVE_HIGH
reset-gpios(lvds_rst) GPIO0_B0 GPIO_ACTIVE_LOW

在硬件信号上LCD_EN引脚高电平有效,具体的引脚配置请参考《GPIO 使用》一节。

背光配置

在DTS文件中配置了背光信息,如下:

backlight1: backlight1 {
		compatible = "pwm-backlight";
		pwms = <&pwm5 0 25000 0>;
		brightness-levels = <
			  0  20  20  21  21  22  22  23
			 23  24  24  25  25  26  26  27
			 27  28  28  29  29  30  30  31
			......
			......
        >;
		default-brightness-level = <200>;
		enable-gpios = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&lcd_en>;
    };
  • enable-gpios:背光使能脚,高电平有效。

  • pwms属性:配置PWM,范例里面默认使用pwm5。

  • brightness-levels属性:配置背光亮度数组,一般以值 255 为一个 scale,当 PWM 设置为正极性时,从 0~255 表示背光为正极,占空比从 0%~100% 变化,255~0 为负极性,占空比从 100%~0% 变化;当 PWM 设置为负极性时,反之。

  • default-brightness-level属性:开机时默认背光亮度,范围为0-255。 具体请参考kernel中的说明文档:kernel/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt

显示时序配置

Power on/off sequence

MIPI屏的上下电时序通常都在规格书的Power on/off sequence章节中,可根据规格书的上下电时序修改dts,panel节点中有如下属性:

&dsi1 {
    ...
	dsi1_panel: panel@0 {
		status = "okay";
		compatible = "simple-panel-dsi";
		reg = <0>;
		backlight = <&backlight1>;
		reset-delay-ms = <60>;
		enable-delay-ms = <60>;
		prepare-delay-ms = <60>;
		unprepare-delay-ms = <60>;
		disable-delay-ms = <60>;
        ...
    };
};

MIPI屏所需时序,调试屏幕时可根据本身上下电时序是否需要再配置此属性。MIPI上下电后需要发送初始化或退出指令才能使之正常工作,列表如下:

&dsi1 {
	status = "okay";
	rockchip,lane-rate = <854>;
	dsi1_panel: panel@0 {
        ...
		panel-init-sequence = [
			23 02 02 27 AA
			23 02 02 48 02
			23 02 02 B6 20
			...
        ];
    };
};

范例 dts 中的 init-sequence ,只是配置了上电指令, 其他的初始化指令已经烧录在 EV101WXM-N10 这款MIPI屏里,dts 无需配置。接下来我们来看看上下电时序在驱动中的实现, 具体实现在kernel/drivers/gpu/drm/panel/panel-simple.c中:

static int panel_simple_disable(struct drm_panel *panel)
{
    ...
	if (p->backlight) {
		p->backlight->props.power = FB_BLANK_POWERDOWN;
		p->backlight->props.state |= BL_CORE_FBBLANK;
		backlight_update_status(p->backlight);
	}

	if (p->desc->delay.disable)
		panel_simple_sleep(p->desc->delay.disable);

	if (p->cmd_type == CMD_TYPE_MCU) {
		err = panel_simple_xfer_mcu_cmd_seq(p, p->desc->exit_seq);
		if (err)
			dev_err(panel->dev, "failed to send exit cmds seq\n");
	}
    ...
}

static int panel_simple_unprepare(struct drm_panel *panel)
{
    ...
	if (p->desc->exit_seq) {
		if (p->dsi)
			panel_simple_xfer_dsi_cmd_seq(p, p->desc->exit_seq);
		else if (p->cmd_type == CMD_TYPE_SPI)
			err = panel_simple_xfer_spi_cmd_seq(p, p->desc->exit_seq);
		if (err)
			dev_err(panel->dev, "failed to send exit cmds seq\n");
	}

	gpiod_direction_output(p->reset_gpio, 1);
	if(!p->enable_on_always){
		gpiod_direction_output(p->enable_gpio, 0);
	}
    ...
}

static int panel_simple_prepare(struct drm_panel *panel)
{
    ...
	gpiod_direction_output(p->enable_gpio, 1);

	if (p->desc->delay.prepare)
		panel_simple_sleep(p->desc->delay.prepare);

    ...
	gpiod_direction_output(p->reset_gpio, 1);

	if (p->desc->delay.reset)
		panel_simple_sleep(p->desc->delay.reset);

	gpiod_direction_output(p->reset_gpio, 0);  //由于驱动在控制reset脚时序的时候使用了`gpiod`这类的API
                                                   //如果在DTS配置了低电平有效,那么这里就会将reset引脚拉高,即取反。
	if (p->desc->delay.init)
		panel_simple_sleep(p->desc->delay.init);

	if (p->desc->init_seq) {
		if (p->dsi)
			panel_simple_xfer_dsi_cmd_seq(p, p->desc->init_seq);
		else if (p->cmd_type == CMD_TYPE_SPI)
			err = panel_simple_xfer_spi_cmd_seq(p, p->desc->init_seq);
		if (err)
			dev_err(panel->dev, "failed to send init cmds seq\n");
	}

    if(p->desc->delay.mipi_data){
        panel_simple_sleep(p->desc->delay.mipi_data);
    }
    ...
}

static int panel_simple_enable(struct drm_panel *panel)
{
    ...
	if (p->cmd_type == CMD_TYPE_MCU) {
		err = panel_simple_xfer_mcu_cmd_seq(p, p->desc->init_seq);
		if (err)
			dev_err(panel->dev, "failed to send init cmds seq\n");
	}
	if (p->desc->delay.enable)
		panel_simple_sleep(p->desc->delay.enable);

	if (p->backlight) {
		p->backlight->props.state &= ~BL_CORE_FBBLANK;
		p->backlight->props.power = FB_BLANK_UNBLANK;
		backlight_update_status(p->backlight);
	}
    ...
}

uboot实现在u-boot/drivers/video/drm/rockchip_panel.c中的panel_simple_unprepare、panel_simple_prepare、panel_simple_enable 、panel_simple_disable 4个函数中,具体实现可以自己查看。

Display-Timings

display_timings在dts中定义:

        disp_timings1: display-timings {
            native-mode = <&dsi1_timing0>;
            dsi1_timing0: timing0 {
                clock-frequency = <72600000>;//<80000000>;
                hactive = <800>;//<768>;
                vactive = <1280>;
                hsync-len = <14>;   //20, 50,10
                hback-porch = <26>; //50, 56,10
                hfront-porch = <32>;//50, 30,180
	            vsync-len = <8>;//4
	            vback-porch = <20>;//4
	            vfront-porch = <80>;//8
	            hsync-active = <0>;
	            vsync-active = <0>;
	            de-active = <0>;
                pixelclk-active = <0>;
            };
        };

相关的参数一般可以在屏的规格书中找到,如下在kernel/drivers/gpu/drm/panel/panel-simple.c的panel_simple_probe中初始化了获取时序的函数。

static int panel_simple_probe(struct device *dev, const struct panel_desc *desc){
...
 panel->base.funcs = &panel_simple_funcs;
...
}
...
static const struct drm_panel_funcs panel_simple_funcs = {
	.loader_protect = panel_simple_loader_protect,
	.disable = panel_simple_disable,
	.unprepare = panel_simple_unprepare,
	.prepare = panel_simple_prepare,
	.enable = panel_simple_enable,
	.get_modes = panel_simple_get_modes,
	.get_timings = panel_simple_get_timings,
};
...

该函数在kernel/drivers/gpu/drm/panel/panel-simple.c中定义:

static int panel_simple_get_timings(struct drm_panel *panel, unsigned int num_timings, struct display_timing *timings)
{
	struct panel_simple *p = to_panel_simple(panel);
	unsigned int i;

	if (p->desc->num_timings < num_timings)
		num_timings = p->desc->num_timings;

	if (timings)
		for (i = 0; i < num_timings; i++)
			timings[i] = p->desc->timings[i];

	return p->desc->num_timings;
}

详细说明可参考以下附件: Rockchip DRM Panel Porting Guide.pdf

EV101WXM-N10屏资料: BOE原装 宽温工业屏 EV101WXM-N10.pdf

FAQ

EV101WXM-N10 10.1寸屏不显示

确认固件是否支持

默认版本无LVDS功能,烧录Neardi-3568 LVDS 固件百度网盘 烧录方法请参阅《使用USB线升级固件》一章。

检查硬件接线是否正确

EV101WXM -> 使用方法 -> 硬件连接 -> LBA3568

检查3.3V跳冒

../../_images/lvds-3-3v.png