环境:rk3399 linux SDK linux kernel版本为4.4.179 uboot版本为201709
问题现象:
uboot阶段 logo.bmp 可以正常显示,并且持续到 logo_kernel.bmp开始显示那一刻, 但是logo_kernel.bmp 虽然能显示,但是一闪而过,之后屏幕就持续黑屏直到显示桌面内容……
问题排查:
查看启动log:
[ 4.212505] ##################show_loader_logo####################### [ 4.262751] ############fb_find_logo depth = 24############# [ 4.262751] Freeing drm_logo memory: 3348K [ 4.325082] Console: switching to colour frame buffer device 100x80 [ 4.375269] rockchip-drm display-subsystem: fb0: frame buffer device
发现drm memory被free,查看 show_loader_logo 函数
struct drm_atomic_state *state, *old_state; struct device_node *np = drm_dev->dev->of_node; struct drm_mode_config *mode_config = &drm_dev->mode_config; struct device_node *root, *route; struct rockchip_drm_mode_set *set, *tmp, *unset; struct list_head mode_set_list; struct list_head mode_unset_list; unsigned plane_mask = 0; int ret; printk("##################%s#######################\r\n",__func__); root = of_get_child_by_name(np, "route"); if (!root) { dev_warn(drm_dev->dev, "failed to parse display resources\n"); return; } if (init_loader_memory(drm_dev)) { dev_warn(drm_dev->dev, "failed to parse loader memory\n"); return; } INIT_LIST_HEAD(&mode_set_list); INIT_LIST_HEAD(&mode_unset_list); drm_modeset_lock_all(drm_dev); state = drm_atomic_state_alloc(drm_dev); if (!state) { dev_err(drm_dev->dev, "failed to alloc atomic state\n"); ret = -ENOMEM; goto err_unlock; } state->acquire_ctx = mode_config->acquire_ctx; for_each_child_of_node(root, route) { if (!of_device_is_available(route)) continue; set = of_parse_display_resource(drm_dev, route); if (!set) continue; if (setup_initial_state(drm_dev, state, set)) { drm_framebuffer_unreference(set->fb); INIT_LIST_HEAD(&set->head); list_add_tail(&set->head, &mode_unset_list); continue; } INIT_LIST_HEAD(&set->head); list_add_tail(&set->head, &mode_set_list); } /* * the mode_unset_list store the unconnected route, if route's crtc * isn't used, we should close it. */ list_for_each_entry_safe(unset, tmp, &mode_unset_list, head) { struct rockchip_drm_mode_set *tmp_set; int found_used_crtc = 0; list_for_each_entry_safe(set, tmp_set, &mode_set_list, head) { if (set->crtc == unset->crtc) { printk("############found 1 used crtc###########\r\n"); found_used_crtc = 1; continue; } } if (!found_used_crtc) { struct drm_crtc *crtc = unset->crtc; int pipe = drm_crtc_index(crtc); struct rockchip_drm_private *priv = drm_dev->dev_private; if (unset->hdisplay && unset->vdisplay){ printk("############close the unused crtc###########\r\n"); priv->crtc_funcs[pipe]->crtc_close(crtc); } } list_del(&unset->head); kfree(unset); } if (list_empty(&mode_set_list)) { dev_warn(drm_dev->dev, "can't not find any loader display\n"); ret = -ENXIO; goto err_free_state; } /* * The state save initial devices status, swap the state into * drm deivces as old state, so if new state come, can compare * with this state to judge which status need to update. */ drm_atomic_helper_swap_state(drm_dev, state); drm_atomic_state_free(state); old_state = drm_atomic_helper_duplicate_state(drm_dev, mode_config->acquire_ctx); if (IS_ERR(old_state)) { dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); ret = PTR_ERR_OR_ZERO(old_state); goto err_free_state; } state = drm_atomic_helper_duplicate_state(drm_dev, mode_config->acquire_ctx); if (IS_ERR(state)) { dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); ret = PTR_ERR_OR_ZERO(state); goto err_free_old_state; } state->acquire_ctx = mode_config->acquire_ctx; list_for_each_entry(set, &mode_set_list, head) /* * We don't want to see any fail on update_state. */ WARN_ON(update_state(drm_dev, state, set, &plane_mask)); ret = drm_atomic_commit(state); drm_atomic_clean_old_fb(drm_dev, plane_mask, ret); list_for_each_entry_safe(set, tmp, &mode_set_list, head) { list_del(&set->head); kfree(set); } /* * Is possible get deadlock here? */ WARN_ON(ret == -EDEADLK); if (ret) { /* * restore display status if atomic commit failed. */ drm_atomic_helper_swap_state(drm_dev, old_state); goto err_free_old_state; } // rockchip_free_loader_memory(drm_dev); // drm_atomic_state_free(old_state); drm_modeset_unlock_all(drm_dev); return; err_free_old_state: drm_atomic_state_free(old_state); err_free_state: drm_atomic_state_free(state); err_unlock: drm_modeset_unlock_all(drm_dev); if (ret) dev_err(drm_dev->dev, "failed to show loader logo\n"); rockchip_free_loader_memory(drm_dev); }
大致的意思是有个备份区存储上个framebuffer内容,如果之后重新分配并且填充了frambuffer则更新备份区内容,也就是显示屏显示内容会更新……
禁止启动阶段fb更新即可解决内核图片一闪而过问题:
RK官方也提供了不少解决方案,但对于我这个现象不适用;这个问题的根源应该是RK为了兼容老的framebuffer机制,如果配置linux内核里面 logo.c 文件里面指定的内核图片显示,但是这会影响到 kernel目录下 logo_kernel.bmp文件的显示,如果不按如上步骤操作, 在logo.c文件内把 fb_logo_late_init 函数里面的 logos_freed 设为false, 则会显示 logo.c 指定的图片,我的版本是几只企鹅…… 启动过程的现象就是:uboot阶段显示logo.bmp正常,接着logo_kernel.bmp一闪而过,接着显示企鹅画面直到进入桌面……
附官方指导文档: