[RK3399][Android7.1] 调试笔记 :UBOOT 中HDMI默认分辨率的修改【UBOOT上HDMI LOGO 显示代码分析】

平台 内核版本 安卓版本
RK3399 Linux4.4 Android7.1

文章目录

    • 问题:
    • 现象:
    • 结论:
    • 修改:
      • 方式一:(修改`uboot`默认`HDMI`分辨率配置)
      • 方式二:(修改代码指定分辨率)
      • 方式三: (修改设备树指定配置)
    • 代码分析(详解):

问题:

我同事的3399开发板外接HDMI显示UBOOT LOGO时不正常。

现象:

显示不正常如下图:
[RK3399][Android7.1] 调试笔记 :UBOOT 中HDMI默认分辨率的修改【UBOOT上HDMI LOGO 显示代码分析】_第1张图片


根据我的上一篇博客的显示诊断:
[px3][Android7.1] 调试笔记display显示屏参设置偏差后现象

结论:

分辨率没有设置正确

修改:


方式一:(修改uboot默认HDMI分辨率配置)

目录rk3399u-boot\drivers\video\rk_hdmi.h
前提:设备树&hdmi
没有指定:rockchip,defaultmode = <16>;

// HDMI default vide mode
#define HDMI_VIDEO_DEFAULT_MODE			HDMI_1280X720P_60HZ//HDMI_1920X1080P_60HZ

改为:

// HDMI default vide mode
#define HDMI_VIDEO_DEFAULT_MODE			HDMI_1920X1080P_60HZ//HDMI_1280X720P_60HZ

方式二:(修改代码指定分辨率)

目录:rk3399u-boot\drivers\video\dw_hdmi\rk_edid.c

const struct hdmi_video_timing* dw_hdmi_vic2timing(struct hdmi_dev *hdmi_dev, int vic)
{
    int i;
    if(vic == 0 || hdmi_dev == NULL)
        return NULL;

	HDMIDBG("%s: modedb len = %d\n", __func__, hdmi_dev->mode_len);
	return &(hdmi_dev->modedb[15]);//直接1920x1080p@60Hz
	/*
    for(i = 0; i < hdmi_dev->mode_len; i++)
    {
        if(hdmi_dev->modedb[i].vic == vic || hdmi_dev->modedb[i].vic_2nd == vic)
            return &(hdmi_dev->modedb[i]);
    }
    return NULL;
    */
}

结构体数组:
目录:rk3399u-boot\drivers\video\dw_hdmi\rk_edid.c

[RK3399][Android7.1] 调试笔记 :UBOOT 中HDMI默认分辨率的修改【UBOOT上HDMI LOGO 显示代码分析】_第2张图片


方式三: (修改设备树指定配置)

修改
设置默认分辨率为HDMI_1920X1080P_60HZ输出:

&hdmi {
    status = "okay";
    rockchip,hdmi_video_source = ;
    rockchip,defaultmode = <16>;
};

目录:kernel/drivers/video/rockchip/hdmi/rockchip-hdmi.h

[RK3399][Android7.1] 调试笔记 :UBOOT 中HDMI默认分辨率的修改【UBOOT上HDMI LOGO 显示代码分析】_第3张图片

解析:
因为:hdmi_find_best_mode函数中:

if (hdmi_dev->defaultmode)
			hdmi_dev->vic = hdmi_dev->defaultmode;
		else
			hdmi_dev->vic = HDMI_VIDEO_DEFAULT_MODE;
		printf("no edid message:use default vic config:%d\n",hdmi_dev->vic);

hdmi_dev->defaultmode在hdmi_parse_dts函数:

		hdmi_dev->defaultmode =
				fdtdec_get_int(gd->fdt_blob, node,
					       "rockchip,defaultmode",
					       0);

hdmi节点中添加 rockchip,defaultmode = 值可以在rockchip-hdmi.hhdmi_video_infomation_code中查到相应值。

hdmi_video_infomation_code 是一个枚举数组,从 1开始,想设置哪个分辨率,value 值就填此分辨率的序号。

设置前首先得支持此分辨率。查看支持的分辨率:cat /sys/class/display/HDMI/modes


修改后正常应该如下图:[RK3399][Android7.1] 调试笔记 :UBOOT 中HDMI默认分辨率的修改【UBOOT上HDMI LOGO 显示代码分析】_第4张图片

代码分析(详解):

board_late_init中调用了board_fbt_preboot();

board_fbt_preboot中:

#if defined(CONFIG_ROCKCHIP_DISPLAY)
		else if (!rockchip_display_init()) {
			g_logo_on_state = 1;
		}
#endif

 gd->uboot_logo = g_logo_on_state;//uboot_logo 状态

重点rockchip_display_init分析:
首先介绍下需要使用的变量:

const void *blob = gd->fdt_blob; device tree
const struct rockchip_connector *conn connector
const struct rockchip_crtc *crtc crtc
struct display_state *s state

1、寻找设备树中的route node.

route = fdt_path_offset(blob, "/display-subsystem/route");

2、判断route node 的status是否是“okay”

	if (!fdt_device_is_available(blob, route))
		return -ENODEV;

3、获取fb地址

init_display_buffer();
--->memory_start = gd->fb_base;

4、进入for循环中

fdt_for_each_subnode(blob, child, route) {
}

fdt_first_subnode获得blob设备树的offset偏移下的节点的第一个子节点
fdt_next_subnode下一个节点

#define fdt_for_each_subnode(fdt, node, parent)		\
	for (node = fdt_first_subnode(fdt, parent);	\
	     node >= 0;					\
	     node = fdt_next_subnode(fdt, node))

4.1、首先查看节点是否是"okay"

if (!fdt_device_is_available(blob, child))
			continue;

4.2、返回节点属性
fdt_getprop_u32_default_node

4.3、获取connect node.
fdt_node_offset_by_phandle

4.4、获取对应的ctrl node并判断是否“okay”
find_crtc_node
fdt_device_is_available

4.5、根据ctrl node从g_crtc数组中找到具体的元素
rockchip_get_crtc

4.6、找connector的结点

conn_node = find_connector_node(blob, connect);
		if (!fdt_device_is_available(blob, conn_node))
			continue;

4.7、据connector node从g_connector数组中找到具体的元素

		conn = rockchip_get_connector(blob, conn_node);
		if (!conn)
			continue;

重点看一下将获得什么?
取出设备树中compatible的名字和g_connector中比对

const struct rockchip_connector *
rockchip_get_connector(const void *blob, int connector_node)
{
	const char *name;
	int i;

	fdt_get_string(blob, connector_node, "compatible", &name);

	for (i = 0; i < ARRAY_SIZE(g_connector); i++) {
		if (!strcmp(name, g_connector[i].compatible))
			break;
	}
	if (i >= ARRAY_SIZE(g_connector))
		return NULL;

	return &g_connector[i];
}

看一下:

static const struct rockchip_connector g_connector[] = {

#ifdef CONFIG_ROCKCHIP_DW_HDMI
	{
	 .compatible = "rockchip,rk3288-dw-hdmi",
	 .funcs = &rockchip_dw_hdmi_funcs,
	 .data = &rk3288_hdmi_drv_data,
	},{
         .compatible = "rockchip,rk3368-dw-hdmi",
         .funcs = &rockchip_dw_hdmi_funcs,
        },{
	 .compatible = "rockchip,rk3399-dw-hdmi",
	 .funcs = &rockchip_dw_hdmi_funcs,
	 .data = &rk3399_hdmi_drv_data,
	}

#endif
};

*4.8、display相关信息都放在struct display_state s中
s = malloc(sizeof(*s));

4.9、初始化链表头
INIT_LIST_HEAD(&s->head);

4.10、分别获取uboot/kernel中的logo name以及模式

fdt_get_string(blob, child, "logo,uboot", &s->ulogo_name);
fdt_get_string(blob, child, "logo,kernel", &s->klogo_name);
fdt_get_string(blob, child, "logo,mode", &name);

4.11、s结构体的赋值

if (!strcmp(name, "fullscreen"))
			s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
		else
			s->logo_mode = ROCKCHIP_DISPLAY_CENTER;
		fdt_get_string(blob, child, "charge_logo,mode", &name);
		if (!strcmp(name, "fullscreen"))
			s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
		else
			s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER;

		s->blob = blob;
		s->conn_state.node = conn_node;
		s->conn_state.connector = conn;//重要(获取分辨率的函数)
		s->crtc_state.node = crtc_node;
		s->crtc_state.crtc = crtc;
		s->crtc_state.crtc_id = get_crtc_id(blob, connect);
		s->node = child;

重点看一下:connfuncs

const struct rockchip_connector_funcs rockchip_dw_hdmi_funcs = {
	.init = rockchip_dw_hdmi_init,
	.deinit = rockchip_dw_hdmi_deinit,
	.prepare = rockchip_dw_hdmi_prepare,
	.enable = rockchip_dw_hdmi_enable,
	.disable = rockchip_dw_hdmi_disable,
	.get_timing = rockchip_dw_hdmi_get_timing,
	.detect = rockchip_dw_hdmi_detect,
	.get_edid = rockchip_dw_hdmi_get_edid,
};

4.12、将s结构体添加到链表中

connector_panel_init(s);
list_add_tail(&s->head, &rockchip_display_list);

那什么时候会调用connector的函数?

rockchip_show_logo中:

void rockchip_show_logo(void)
{
	struct display_state *s;

	list_for_each_entry(s, &rockchip_display_list, head) {
		s->logo.mode = s->logo_mode;
		if (load_bmp_logo(&s->logo, s->ulogo_name)) {
			printf("failed to display uboot logo\n");
			continue;
		}
		display_logo(s);
		if (load_bmp_logo(&s->logo, s->klogo_name))
			printf("failed to display kernel logo\n");
	}
}

为了能正常显示希望这里的s->logo_mode等于15

display_logo的时候会调用display_init(state);

display_init中:

if (conn_funcs->get_timing) {
		ret = conn_funcs->get_timing(state);
		if (ret)
			goto deinit_conn;
	} else {
		ret = display_get_timing(state);
		if (ret)
			goto deinit_conn;
	}

hdmi显示:rockchip_dw_hdmi_get_timing

中:videomode = hdmi_edid_get_timing(hdmi_dev);

屏参:

	mode->hdisplay = videomode->xres;
	mode->hsync_start = mode->hdisplay + videomode->right_margin;
	mode->hsync_end = mode->hsync_start + videomode->hsync_len;
	mode->htotal = mode->hsync_end + videomode->left_margin;
	mode->vdisplay = videomode->yres;
	mode->vsync_start = mode->vdisplay + videomode->lower_margin;
	mode->vsync_end = mode->vsync_start + videomode->vsync_len;
	mode->vtotal = mode->vsync_end + videomode->upper_margin;
	mode->clock = videomode->pixclock / 1000;

重点再分析下
hdmi_edid_get_timing

const struct video_mode* hdmi_edid_get_timing(struct hdmi_dev *hdmi_dev)
{
	const struct hdmi_video_timing *timing = NULL;
	const struct hdmi_video *vpara;
	const struct video_mode *videomode = NULL;

	hdmi_parse_edid(hdmi_dev);
	hdmi_find_best_mode(hdmi_dev);
	vpara = &hdmi_dev->video;
	timing = dw_hdmi_vic2timing(hdmi_dev, vpara->vic);
	if (timing == NULL)
		printf("[%s] not found vic %d\n", __func__, vpara->vic);
	else
		videomode = &(timing->mode);

	return videomode;
}
const struct hdmi_video_timing* dw_hdmi_vic2timing(struct hdmi_dev *hdmi_dev, int vic)
{
    int i;
    if(vic == 0 || hdmi_dev == NULL)
        return NULL;

	HDMIDBG("%s: modedb len = %d\n", __func__, hdmi_dev->mode_len);

    for(i = 0; i < hdmi_dev->mode_len; i++)
    {
        if(hdmi_dev->modedb[i].vic == vic || hdmi_dev->modedb[i].vic_2nd == vic)
            return &(hdmi_dev->modedb[i]);
    }
    return NULL;
}

其中模式还由:void hdmi_find_best_mode(struct hdmi_dev *hdmi_dev)处理

这个函数跟我的上一篇博客类似:
[PX3][Android7.1] 调试笔记 — HDMI 自动适配过程分析

void hdmi_find_best_mode(struct hdmi_dev *hdmi_dev)
{
	int i = 0, pos_baseparamer = 0;
	int deepcolor;

	hdmi_check_edid_mode(hdmi_dev);
	pos_baseparamer = g_pos_baseparamer.hdmi_pos;

#ifdef HDMIDEBUG
	for (i = 0; i < hdmi_dev->vic_pos; i++) {
		printf("%d", hdmi_dev->vicdb[i] & HDMI_VIC_MASK);
		if (hdmi_dev->vicdb[i] & HDMI_VIDEO_YUV420)
			printf("(yuv420)");
		printf(" ");
	}
	printf("\n");
#endif
	/*if read edid error,use default vic mode, or not check pos_baseparamer and selete best video mode*/
	if (hdmi_dev->vic_pos > 0) {
		hdmi_dev->video.sink_hdmi = hdmi_dev->driver.edid.sink_hdmi;
		if (pos_baseparamer >= 0) {
			for(i = 0; i < hdmi_dev->vic_pos; i++) {
				if (hdmi_dev->base_paramer_hdmi.interlaced & HDMI_VIDEO_YUV420) {
					if (hdmi_dev->vicdb[i] == (hdmi_dev->modedb[pos_baseparamer].vic | HDMI_VIDEO_YUV420) ||
					    hdmi_dev->vicdb[i] == (hdmi_dev->modedb[pos_baseparamer].vic_2nd | HDMI_VIDEO_YUV420))
						break;
				} else {
					if (hdmi_dev->vicdb[i] == hdmi_dev->modedb[pos_baseparamer].vic ||
					    hdmi_dev->vicdb[i] == hdmi_dev->modedb[pos_baseparamer].vic_2nd)
						break;
				}
			}
			if (i < hdmi_dev->vic_pos) {
				hdmi_dev->vic = hdmi_dev->vicdb[i];
				printf("use baseparamer config,pos_baseparamer=%d\n",pos_baseparamer);
			} else if (hdmi_find_nearest_mode(hdmi_dev, pos_baseparamer)) {
				hdmi_find_best_edid_mode(hdmi_dev);
				printf("pos_baseparamer=%d,but edid not support,find best edid vic=%d\n",
					pos_baseparamer,hdmi_dev->vic);
			} else {
				printf("pos_baseparamer=%d,but edid not support,find nearest edid vic=%d\n",
				       pos_baseparamer,hdmi_dev->vic);
			}
		} else {
			hdmi_find_best_edid_mode(hdmi_dev);
			printf("no baseparametr,find best edid mode,vic=%d\n",hdmi_dev->vic);
		}
	} else {
		if (hdmi_dev->defaultmode)
			hdmi_dev->vic = hdmi_dev->defaultmode;
		else
			hdmi_dev->vic = HDMI_VIDEO_DEFAULT_MODE;
		printf("no edid message:use default vic config:%d\n",hdmi_dev->vic);
	}
	
	hdmi_dev->video.vic = hdmi_dev->vic & HDMI_VIC_MASK;
	printf("hdmi_dev->video.vic is %d\n", hdmi_dev->video.vic);
	if (hdmi_dev->video.sink_hdmi == 0) {
		hdmi_dev->video.color_output = HDMI_COLOR_RGB_0_255;
		hdmi_dev->video.color_input = HDMI_COLOR_RGB_0_255;
	} else {
		if (hdmi_dev->driver.edid.ycbcr444)
			hdmi_dev->video.color_output = HDMI_COLOR_YCBCR444;
		else if (hdmi_dev->driver.edid.ycbcr444)
			hdmi_dev->video.color_output = HDMI_COLOR_YCBCR422;

		if (hdmi_dev->vic & HDMI_VIDEO_YUV420) {
			hdmi_dev->video.color_output = HDMI_COLOR_YCBCR420;
			hdmi_dev->video.color_input = HDMI_COLOR_YCBCR420;
			deepcolor = hdmi_dev->driver.edid.deepcolor_420;
		} else {
			deepcolor = hdmi_dev->driver.edid.deepcolor;
		}

		if (hdmi_dev->feature & SUPPORT_YCBCR_INPUT) {
			if (hdmi_dev->video.color_output == HDMI_COLOR_YCBCR444 ||
			    hdmi_dev->video.color_output == HDMI_COLOR_YCBCR422)
				hdmi_dev->video.color_input = HDMI_COLOR_YCBCR444;
			else if (hdmi_dev->video.color_output == HDMI_COLOR_YCBCR420)
				hdmi_dev->video.color_input = HDMI_COLOR_YCBCR420;
		}
		/*if ((hdmi_dev->feature & SUPPORT_DEEP_10BIT) &&
		    (deepcolor & HDMI_DEEP_COLOR_30BITS))
			hdmi_dev->video.color_output_depth = 10;
		else*/
		hdmi_dev->video.color_output_depth = 8;
	}
}

uboot 阶段 edid 没有读到,因此无法自适配。

你可能感兴趣的:(子类__Uboot,子类__Display)