方案介绍
基于 RK3568 在 kernel 阶段显示开机 logo 动画,本质原理是将 logo 图片采用刷新的方式演示成为开机动画的效果。kernel 版本采用 4.19 版本(5.10 同样适用)。
本文将首先描述如何将一张自定义的图片修改添加到 kernel 中称为 kernel 的开机动画(该方式将不仅仅是在 kernel 路径下替换 logo.bmp 这么简单)。本文主要内容如下:
- kernel 层开机 logo 显示
- 修改代码,实现开机动画效果
- 基于 RK3568 SDK 下配置增加 logo 图片资源分区,实现后期简单可替换的 logo 开机动画方式
kernel 层开机 logo 显示
这里采用 Linux kernel 自身设定的开机 logo 方式,所以 logo 的文件名为 logo_linux_clut224.ppm,该文件一般会存放在 kernel/drivers/video/logo/
路径下。采用 netpbm
工具生成想要替换的 logo(注意图片的尺寸大小,参考显示屏幕的尺寸)。
- logo 格式转换(Linux 只支持 ppm 格式的图片,需要将 png / bmp 格式转换成 ppm)
# png 格式
pngtopnm logo.png > logo.pnm
# bmp 格式
bmptopnm logo.bmp > logo.pnm
# 下面步骤一致
pnmquant 224 logo.pnm > logo224.pnm
pnmtoplainpnm logo224.pnm > logo_linux_clut224.ppm
以上转换完成后,将生成的 logo_linux_clut224.ppm
放入 kernel/drivers/video/logo/
路径下(会替换原有的 logo_linux_clut224.ppm),在编译时会生成对应的 .c 文件和 .o 文件,可能会出现如下错误:
kernel/drivers/video/logo/logo_linux_clut224.ppm: Binary PNM is not supported
Use pnmnoraw(1) to convert it to ASCII PNM
以上错误是因为 ppm 图片是 bin 格式,需要转换成 ASCII 格式,方法如下:
pnmnoraw logo_linux_no_ascii_clut224.ppm > logo_linux_clut224.ppm
替换目录中的文件重新编译即可。
- 调整显示位置
默认是左上角对齐,调整为中心对齐的方法如下:
修改drivers/video/fbmem.c
文件的fb_show_logo_line()
函数:
修改其中画住位置的代码如下:
image.dx = (info->var.xres / 2) - (image.width / 2); //中心
image.dy = (info->var.yres / 2) - (image.height/ 2); //中心
image.width = logo->width;
image.height = logo->height;
注意:info->var.xres
和 info->var.yres
是分辨率大小,image.width
、image.height
是 logo 图片的大小。
图片显示起点位置修改,修改 drivers/video/console/fbcon.c
文件的 fbcon_prepare_logo()
函数:
根据图片大小修改起点位置如下:
logo_height = fb_prepare_logo(info, ops->rotate);
logo_height += (info->var.yres / 2) - (logo_height / 2);
- 配置 kernel config ,启用 kernel logo:
Device Drivers--->
Graphics support--->
[*]Boot logo--->
[*]Standard 224-color Linux logo
需要添加支持 kernel console
,不选会看不到 logo:
Device Drivers--->
Graphics support--->
Console display driver support--->
<*>Framebuffer Console support
全屏显示时,屏幕左上角会有光标闪烁的问题,解决方法如下:
修改 drivers/video/fbdev/core/bitblit.c
中的 bit_cursor()
函数:
将这个判断注释掉,或者将 err
改为 0
, 如下:
修改代码,实现开机动画效果
通过分析代码,可以发现显示 logo 的位置在 drivers/video/fbdev/core/fbcon.c
文件中的 fbcon_switch()
函数中,具体位置如下:
可以看到其中的 fb_show_logo()
函数,进入这个函数,位置在 drivers/video/fbdev/core/fbmem.c
,代码如下:
通过以上函数可以发现,显示 logo 资源数据来自 fb_logo.logo
,在这里,我们只需要修改 fb_logo.logo
资源就可以实现动态的 logo 显示。
通过查找代码,发现加载 logo 资源的代码来自于 drivers/video/fbdev/core/fbcon.c
文件的 fbcon_prepare_logo()
函数中,参考流程如下:
fbcon_prepare_logo()
--->fb_prepare_logo()
--->fb_find_logo() [这个函数位于 drivers/video/logo/logo.c 中]
通过上面的代码执行流程可以得到 fb_logo.logo
资源。所以,如果想要实现动态 logo 显示,只需要使用多个 logo 资源来循环显示就可以实现动画效果。因此,我们可以循环调用 fb_find_logo()
来实现。问题来了,现在我们 fb_find_logo(int depth)
的参数是 depth 这个值,本身 kernel 查找 logo 资源是通过这个值来找到不同颜色深度的图片资源,通常系统中使用 depth 这个值只会用到低 16 位,我们可以使用这个 depth 参数的高 16 位用于传递动态显示图片的图片序号值。
在 drivers/video/logo/logo.c
文件中的 fb_find_logo()
函数修改如下:
可以看出,通过提取 depth 的高 16 位作为 num 来标识图片序号。至于后面的 logo 数据,参考这里的 logo_linux_clut224(上一章内容中添加 logo 资源的方式进行添加)。
注意:编译配置为:Standard 224-color Linux logo 时,logo 资源数据文件一定要以 _clut224.ppm 结尾,否则 Makefile 识别不到,具体内容可以参考 drivers/video/logo/
路径下的 Makefile 文件中关于 ppm 文件的编译规则:
到这里,我们就可以编写函数来实现循环获取 logo 资源用于动画显示了。这里我们不修改 fbcon_prepare_logo()
函数,重新实现一个获取 logo 资源的 fb_get_logo()
函数,这是因为在 fbcon_prepare_logo()
中有很多操作是不需要我们去修改的,所以仅仅在 fb_show_logo
之前更新 fb_logo.logo
资源即可。
在 drivers/video/fbdev/core/fbmem.c
文件中添加如下的新函数:
需要注意我们使用该函数的位置是在 drivers/video/fbdev/core/fbcon.c
中,所以在这里还需要使用 EXPORT_SYMBOL(fb_get_logo);
将这个函数导出,否则编译的时候会提示未定义的引用。
现在一切就绪,我们就可以做动态显示了。回到 logo 显示的 drivers/video/fbdev/core/fbcon.c
文件中的 fbcon_switch()
函数中,做如下修改:
static int fbcon_switch(struct vc_data *vc)
{
... ...
if(logo_shown == FBCON_LOGO_DRAW) {
logo_shown = fg_console;
/* This is protected above by initmem_freed*/
for(i = 0; i <