今天继续分析分析源码,看看高通8909的aboot是如何显示logo的。既然涉及到aboot,那么我们就从aboot的入口函数aboot_init开始分析。
aboot_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
void aboot_init(const struct app_descriptor *app) { .......... // 前面无关代码略过 // 从devinfo分区读取设备信息,在后面初始化panel的时候要用到 read_device_info(&device); read_allow_oem_unlock(&device); .......... // 略过 // 按条件设置display panel #if DISPLAY_SPLASH_SCREEN #if NO_ALARM_DISPLAY if (!check_alarm_boot()) { #endif dprintf(SPEW, "Display Init: Start\n"); #if DISPLAY_HDMI_PRIMARY if (!strlen(device.display_panel)) strlcpy(device.display_panel, DISPLAY_PANEL_HDMI, sizeof(device.display_panel)); #endif // ENABLE_WBC只在8996的芯片上才设置为true,所以这里我们走#else #if ENABLE_WBC /* Wait if the display shutdown is in progress */ while(pm_app_display_shutdown_in_prgs()); if (!pm_appsbl_display_init_done()) target_display_init(device.display_panel); else display_image_on_screen(); #else target_display_init(device.display_panel); #endif dprintf(SPEW, "Display Init: Done\n"); #if NO_ALARM_DISPLAY } #endif #endif .............. // 后面代码略过 |
aboot_init —> target_display_init
target_display_init函数在lk/target/msm8909文件夹内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
void target_display_init(const char *panel_name) { uint32_t panel_loop = 0; uint32_t ret = 0; struct oem_panel_data oem; // 根据devinfo中的panel信息,选择对应panel set_panel_cmd_string(panel_name); oem = mdss_dsi_get_oem_data(); dprintf(INFO, "target_display_init panel name:%s\n", oem.panel); if (!strcmp(oem.panel, NO_PANEL_CONFIG) || !strcmp(oem.panel, SIM_VIDEO_PANEL) || !strcmp(oem.panel, SIM_CMD_PANEL) || oem.skip) { dprintf(INFO, "Selected %s: Skip panel configuration\n", oem.panel); return; } // target_splash_disable返回false if (target_splash_disable()) return; do { // 设置splash_override为false target_force_cont_splash_disable(false); // 重要函数,下面分析 ret = gcdb_display_init(oem.panel, MDP_REV_305, MIPI_FB_ADDR); if (!ret || ret == ERR_NOT_SUPPORTED) { break; } else { target_force_cont_splash_disable(true); msm_display_off(); } } while (++panel_loop <= oem_panel_max_auto_detect_panels()); if (!oem.cont_splash) { dprintf(INFO, "Forcing continuous splash disable\n"); target_force_cont_splash_disable(true); } } |
target_display_init —> gcdb_display_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
int gcdb_display_init(const char *panel_name, uint32_t rev, void *base) { int ret = NO_ERROR; int pan_type; dsi_video_mode_phy_db.pll_type = DSI_PLL_TYPE_28NM; pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db); // 根据panel的不同,设置对应参数,这里就不贴出来了 if (pan_type == PANEL_TYPE_DSI) { ................ } else if (pan_type == PANEL_TYPE_EDP) { ................. } else { dprintf(CRITICAL, "Target panel init not found!\n"); ret = ERR_NOT_SUPPORTED; goto error_gcdb_display_init; } panel.fb.base = base; panel.mdp_rev = rev; // 下面重点分析 ret = msm_display_init(&panel); error_gcdb_display_init: display_enable = ret ? 0 : 1; return ret; } |
gcdb_display_init —> msm_display_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
int msm_display_init(struct msm_fb_panel_data *pdata) { int ret = NO_ERROR; panel = pdata; if (!panel) { ret = ERR_INVALID_ARGS; goto msm_display_init_out; } // 这部分的代码是根据之前设置的参数点亮屏幕 if (pdata->power_func) ret = pdata->power_func(1, &(panel->panel_info)); if (ret) goto msm_display_init_out; if (pdata->dfps_func) ret = pdata->dfps_func(&(panel->panel_info)); /* Enable clock */ if (pdata->clk_func) ret = pdata->clk_func(1, &(panel->panel_info)); if (ret) goto msm_display_init_out; /* Read specifications from panel if available. * If further clocks should be enabled, they can be enabled * using pll_clk_func */ if (pdata->update_panel_info) ret = pdata->update_panel_info(); if (ret) goto msm_display_init_out; /* Enabled for auto PLL calculation or to enable * additional clocks */ if (pdata->pll_clk_func) ret = pdata->pll_clk_func(1, &(panel->panel_info)); if (ret) goto msm_display_init_out; /* pinfo prepare */ if (pdata->panel_info.prepare) { /* this is for edp which pinfo derived from edid */ ret = pdata->panel_info.prepare(); panel->fb.width = panel->panel_info.xres; panel->fb.height = panel->panel_info.yres; panel->fb.stride = panel->panel_info.xres; panel->fb.bpp = panel->panel_info.bpp; } if (ret) goto msm_display_init_out; ret = msm_fb_alloc(&(panel->fb)); if (ret) goto msm_display_init_out; // 设置屏幕Framebuffer显示模式,RGB555 or RGB888 fbcon_setup(&(panel->fb)); // 下面接着分析 display_image_on_screen(); ................... } |
msm_display_init —> display_image_on_screen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void display_image_on_screen(void) { // DISPLAY_TYPE_MIPI为true #if DISPLAY_TYPE_MIPI // 函数声明,fetch_image_from_partition定义在aboot.c中 int fetch_image_from_partition(); // 根据fetch_image_from_partition返回值分两种情况 if (fetch_image_from_partition() < 0) { display_default_image_on_screen(); } else { /* data has been put into the right place */ fbcon_flush(); } #else display_default_image_on_screen(); #endif } |
display_image_on_screen —> fetch_image_from_partition
1 2 3 4 5 6 7 8 9 |
int fetch_image_from_partition() { // 使用emmc,走true分支 if (target_is_emmc_boot()) { return splash_screen_mmc(); } else { return splash_screen_flash(); } } |
fetch_image_from_partition —> splash_screen_mmc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
int splash_screen_mmc() { int index = INVALID_PTN; unsigned long long ptn = 0; struct fbcon_config *fb_display = NULL; struct logo_img_header *header; uint32_t blocksize, realsize, readsize; uint8_t *base; // 获取splash分区的index index = partition_get_index("splash"); if (index == 0) { dprintf(CRITICAL, "ERROR: splash Partition table not found\n"); return -1; } // 获取分区偏移 ptn = partition_get_offset(index); if (ptn == 0) { dprintf(CRITICAL, "ERROR: splash Partition invalid\n"); return -1; } // 设置读取的lun mmc_set_lun(partition_get_lun(index)); // 获取emmc的块大小 blocksize = mmc_get_device_blocksize(); if (blocksize == 0) { dprintf(CRITICAL, "ERROR:splash Partition invalid blocksize\n"); return -1; } // 获取framebuffer配置 fb_display = fbcon_display(); if (!fb_display) { dprintf(CRITICAL, "ERROR: fb config is not allocated\n"); return -1; } // framebuffer内存地址 base = (uint8_t *) fb_display->base; // 读取splash分区头信息到framebuffer中 if (mmc_read(ptn, (uint32_t *)(base + LOGO_IMG_OFFSET), blocksize)) { dprintf(CRITICAL, "ERROR: Cannot read splash image header\n"); return -1; } // 判断splash分区头信息是否有误 header = (struct logo_img_header *)(base + LOGO_IMG_OFFSET); if (splash_screen_check_header(header)) { dprintf(CRITICAL, "ERROR: Splash image header invalid\n"); return -1; } if (fb_display) { // 判断logo的格式,我们在使用logo_gen.py脚本制作splash.img时就有做RLE压缩 // 所以这里选择第一种情况 if (header->type && (header->blocks != 0)) { /* 1 RLE24 compressed data */ base += LOGO_IMG_OFFSET; // logo真实大小 realsize = header->blocks * 512; readsize = ROUNDUP((realsize + LOGO_IMG_HEADER_SIZE), blocksize) - blocksize; /* if the logo is not full-screen size, clean screen */ if ((header->width != fb_display->width) || (header->height != fb_display->height)) fbcon_clear(); uint32_t fb_size = ROUNDUP(fb_display->width * fb_display->height * (fb_display->bpp / 8), 4096); // logo大小过大 if (readsize > fb_size) { dprintf(CRITICAL, "ERROR: Splash image size invalid\n"); return -1; } // 读取到framebuffer中 if (mmc_read(ptn + blocksize, (uint32_t *)(base + blocksize), readsize)) { dprintf(CRITICAL, "ERROR: Cannot read splash image from partition\n"); return -1; } // 解压 fbcon_extract_to_screen(header, (base + LOGO_IMG_HEADER_SIZE)); } else { /* 2 Raw BGR data */ ................... // 省略 } } // 最后返回0 return 0; } |
所以只有成功读取到splash分区,并且分区中的logo的信息没有错误,最后才会返回0,否则都会返回-1。下面我们返回display_image_on_screen看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void display_image_on_screen(void) { // DISPLAY_TYPE_MIPI为true #if DISPLAY_TYPE_MIPI // 函数声明,fetch_image_from_partition定义在aboot.c中 int fetch_image_from_partition(); // 根据fetch_image_from_partition返回值分两种情况 if (fetch_image_from_partition() < 0) { // 要是读取splash分区出错,我们就走default流程 display_default_image_on_screen(); } else { // 所以,如果上面读取splash分区无误,我们就显示到屏幕上 fbcon_flush(); } #else display_default_image_on_screen(); #endif } |
下面我们看看default模式显示logo是怎么个处理法。
display_image_on_screen —> display_default_image_on_screen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
void display_default_image_on_screen(void) { unsigned i = 0; unsigned total_x; unsigned total_y; unsigned bytes_per_bpp; unsigned image_base; #if DISPLAY_TYPE_MIPI char *image = NULL; #endif // 找不到framebuffer配置 if (!config) { dprintf(CRITICAL,"NULL configuration, image cannot be displayed\n"); return; } // 清屏 fbcon_clear(); total_x = config->width; total_y = config->height; bytes_per_bpp = ((config->bpp) / 8); image_base = ((((total_y/2) - (SPLASH_IMAGE_HEIGHT / 2) - 1) * (config->width)) + (total_x/2 - (SPLASH_IMAGE_WIDTH / 2))); #if DISPLAY_TYPE_MIPI #if ENABLE_WBC image = (pm_appsbl_charging_in_progress() ? image_batt888 : imageBuffer_rgb888); #else // 走这里,将image内容设置为imageBuffer_rgb888 // imageBuffer_rgb888是一个大数组,记录logo的信息 image = imageBuffer_rgb888; #endif // end ENABLE_WBC if (bytes_per_bpp == 3) { // 直接将imageBuffer_rgb888数组的内容拷贝到framebuffer中 for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) { memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp), image + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp), SPLASH_IMAGE_WIDTH * bytes_per_bpp); } } fbcon_flush(); #if DISPLAY_MIPI_PANEL_NOVATEK_BLUE if(is_cmd_mode_enabled()) mipi_dsi_cmd_mode_trigger(); #endif // end DISPLAY_MIPI_PANEL_NOVATEK_BLUE #else // else DISPLAY_TYPE_MIPI if (bytes_per_bpp == 2) { for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) { memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp), imageBuffer + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp), SPLASH_IMAGE_WIDTH * bytes_per_bpp); } } fbcon_flush(); #endif } |
到此,显示logo的内容就完了。总结来说,显示logo有两种方法,第一种就是添加一个splash分区,将logo制作成splash.img镜像,然后刷到splash分区;另一种就是讲logo转换并填充到imageBuffer_rgb888数组即可。了解了原理,下面我们就来实践一下吧。高通MSM8909的代码都没有实现,需要自己实现。
方法一:转换成imageBuffer_rgb888数组
好吧,这种方法当然是可以的。但是试了几次之后发现,这种方法只适合分辨率较小的logo图,博主试过720×1280、480×800和200×320这三个,发现就只有200×320可以显示成功,所以这种方法并不适合分辨率较高的logo。
首先是准备好自己的logo图像,这里假设图像文件名为logo.png,依次执行下面三部得到素组
- convert logo.png -recolor “0 0 1,0 1 0,1 0 0” logo_re.png
- convert -depth 8 logo_re.png rgb:logo_raw.raw
- xxd -i logo_raw.raw > splash_test.h
这里得到的splash_test.h文件内就包含我们需要的logo数组了。我们只需将里面的数组替换到lk/platform/msm_share/splash.h中的imageBuffer_rgb888数组中即可,当然要记得修改SPLASH_IMAGE_WIDTH和SPLASH_IMAGE_HEIGHT这两个宏为图像实际分辨率。
方法二:使用splash.img
首先在device/qcom/common/generate_extra_images文件中添加splash.img的编译选项,添加的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#---------------------------------------------------------------------- # Generate splash image (splash.img) #---------------------------------------------------------------------- INSTALLED_SPLASHIMAGE_TARGET := $(PRODUCT_OUT)/splash.img define build-splashimage-target $(hide) python device/qcom/common/display/logo/logo_gen.py device/qcom/msm8909test/logo.png $(hide) cp splash.img $(PRODUCT_OUT)/ $(hide) rm splash.img endef $(INSTALLED_SPLASHIMAGE_TARGET): $(build-splashimage-target) ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_SPLASHIMAGE_TARGET) ALL_MODULES.$(LOCAL_MODULE).INSTALLED += $(INSTALLED_SPLASHIMAGE_TARGET) |
其次是,根据上面填写的内容,我们要见logo图像命名为logo.png,同时将logo图像放到device/qcom/msm8909test/logo.png下,各位可以根据自己项目的需要更换这个路径
最后就是修改partition.xml,默认的partition.xml文件是这样的
1 2 3 |
<partition label="ssd" size_in_kb="8" type="2C86E742-745E-4FDD-BFD8-B6A7AC638772" bootable="false" readonly="false" filename=""/> <partition label="splash" size_in_kb="10240" type="20117f86-E985-4357-B9EE-374BC1D8487D" bootable="false" readonly="false" filename=""/> <partition label="keystore" size_in_kb="512" type="DE7D4029-0F5B-41C8-AE7E-F6C023A02B33" bootable="false" readonly="false" filename="" /> |
可以看出,在执行flattern的时候并不会获取hlos中编译的splash.img,所以这里我们将splash.img加上去即可。
这样使用方法二来显示lk的logo就完成了。