平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | Android7.1 |
我同事的3399开发板外接HDMI
显示UBOOT
LOGO
时不正常。
根据我的上一篇博客的显示诊断:
[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
修改
设置默认分辨率为HDMI_1920X1080P_60HZ
输出:
&hdmi {
status = "okay";
rockchip,hdmi_video_source = ;
rockchip,defaultmode = <16>;
};
目录:kernel/drivers/video/rockchip/hdmi/rockchip-hdmi.h
解析:
因为: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.h
的 hdmi_video_infomation_code
中查到相应值。
hdmi_video_infomation_code
是一个枚举数组,从 1
开始,想设置哪个分辨率,value
值就填此分辨率的序号。
设置前首先得支持此分辨率。查看支持的分辨率:cat /sys/class/display/HDMI/modes
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;
重点看一下:conn
的funcs
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 没有读到,因此无法自适配。