From 9a8ba5b39ac0eaa3370f9be46ee022aa5497890e Mon Sep 17 00:00:00 2001 From: Kapil Porwal Date: Wed, 9 Jul 2025 10:42:12 +0530 Subject: [PATCH] {lib, drivers/intel}: Move BMP rendering logic out of SoC code This patch refactors the BMP rendering logic, moving it from drivers/intel/fsp2_0 to src/lib. This centralizes the code responsible for rendering BMP images to the framebuffer. Key changes: - Move BMP rendering functions (e.g., calculate_logo_coordinates, copy_logo_to_framebuffer) and their dependencies to src/lib/render_bmp.c and src/lib/render_bmp.h. - Decouple BMP definitions from UEFI headers by introducing new coreboot-specific structures for BMP images and BLT pixels. - Consolidate bootsplash-related declarations into bootsplash.h, including new `fw_splash_vertical_alignment`, `fw_splash_horizontal_alignment`, and `struct logo_config`. - Update `soc_load_logo_by_coreboot` to use the new common `load_and_render_logo_to_framebuffer` function and `struct logo_config` for rendering. - Relocate `release_logo` to `src/lib/render_bmp.c` for better module structure. - Update `src/lib/Makefile.mk` to include the new render_bmp.c. This refactoring improves code organization and reusability, making BMP rendering accessible without tight coupling to Intel-specific driver code. BUG=b:427387842 TEST=Verify firmware splash screen on google/fatcat. Change-Id: I0e20ea7e44b4b3ccdb2d4aa9b6aa10ed3447ccfc Signed-off-by: Kapil Porwal Reviewed-on: https://review.coreboot.org/c/coreboot/+/88361 Reviewed-by: Subrata Banik Tested-by: build bot (Jenkins) --- src/drivers/intel/fsp2_0/cb_logo.c | 207 +---- src/drivers/intel/fsp2_0/fsp_gop_blt.c | 448 +---------- src/drivers/intel/fsp2_0/silicon_init.c | 9 - src/include/bootsplash.h | 120 +++ src/lib/Makefile.mk | 1 + src/lib/render_bmp.c | 721 ++++++++++++++++++ src/lib/render_bmp.h | 58 ++ .../common/block/include/intelblocks/cfg.h | 101 +-- 8 files changed, 918 insertions(+), 747 deletions(-) create mode 100644 src/lib/render_bmp.c create mode 100644 src/lib/render_bmp.h diff --git a/src/drivers/intel/fsp2_0/cb_logo.c b/src/drivers/intel/fsp2_0/cb_logo.c index d533806656..c77e55eeed 100644 --- a/src/drivers/intel/fsp2_0/cb_logo.c +++ b/src/drivers/intel/fsp2_0/cb_logo.c @@ -17,11 +17,6 @@ #include #include -struct logo_coordinates { - uint32_t x; - uint32_t y; -}; - /* * Programs the Local Memory BAR (LMEMBAR) for the IGD. * @@ -44,169 +39,12 @@ static void program_igd_lmembar(uint32_t base) pci_or_config16(SA_DEV_IGD, PCI_COMMAND, enable_mask); } -/* - * Calculates the destination coordinates for the logo based on both horizontal and - * vertical alignment settings. - * - * horizontal_resolution: The horizontal resolution of the display panel. - * vertical_resolution: The vertical resolution of the display panel. - * logo_width: The width of the logo bitmap. - * logo_height: The height of the logo bitmap. - * halignment: The horizontal alignment setting. Use FW_SPLASH_HALIGNMENT_NONE - * if only vertical alignment is specified. - * valignment: The vertical alignment setting. Use FW_SPLASH_VALIGNMENT_NONE - * if only horizontal alignment is specified. - * - * Returning `struct logo_coordinates` that contains the calculated x and y coordinates - * for rendering the logo. - */ -static struct logo_coordinates calculate_logo_coordinates( - uint32_t horizontal_resolution, uint32_t vertical_resolution, - uint32_t logo_width, uint32_t logo_height, - enum fw_splash_vertical_alignment valignment, - enum fw_splash_horizontal_alignment halignment) -{ - struct logo_coordinates coords; - - /* Calculate X coordinate */ - switch (halignment) { - case FW_SPLASH_HALIGNMENT_LEFT: - coords.x = 0; - break; - case FW_SPLASH_HALIGNMENT_RIGHT: - coords.x = horizontal_resolution - logo_width; - break; - default: /* FW_SPLASH_HALIGNMENT_CENTER (default) */ - coords.x = (horizontal_resolution - logo_width) / 2; - break; - } - - /* Calculate Y coordinate */ - switch (valignment) { - case FW_SPLASH_VALIGNMENT_MIDDLE: - coords.y = vertical_resolution / 2; - break; - case FW_SPLASH_VALIGNMENT_TOP: - coords.y = 0; - break; - case FW_SPLASH_VALIGNMENT_BOTTOM: - coords.y = vertical_resolution - logo_height; - break; - default: /* FW_SPLASH_VALIGNMENT_CENTER (default) */ - coords.y = (vertical_resolution - logo_height) / 2; - break; - } - - return coords; -} - -/* - * Copies the logo to the framebuffer. - * - * framebuffer_base: The base address of the framebuffer. - * bytes_per_scanline: The number of bytes per scanline in the framebuffer. - * logo_buffer_addr: The address of the logo data in BLT format. - * logo_width: The width of the logo bitmap. - * logo_height: The height of the logo bitmap. - * dest_x: The destination x-coordinate in the framebuffer for rendering the logo. - * dest_y: The destination y-coordinate in the framebuffer for rendering the logo. - */ -static void copy_logo_to_framebuffer( - uintptr_t framebuffer_base, uint32_t bytes_per_scanline, - efi_uintn_t logo_buffer_addr, uint32_t logo_width, uint32_t logo_height, - efi_uintn_t dest_x, efi_uintn_t dest_y) -{ - size_t pixel_size = sizeof(efi_graphics_output_blt_pixel); - size_t logo_line_bytes = logo_width * pixel_size; - efi_uintn_t framebuffer_offset = framebuffer_base + dest_y * bytes_per_scanline - + dest_x * pixel_size; - uint8_t *dst_row_address = (uint8_t *)framebuffer_offset; - uint8_t *src_row_address = (uint8_t *)(uintptr_t)logo_buffer_addr; - for (uint32_t i = 0; i < logo_height; i++) { - memcpy(dst_row_address, src_row_address, logo_line_bytes); - dst_row_address += bytes_per_scanline; - src_row_address += logo_line_bytes; - } -} - -/* - * Loads, converts, and renders a BMP logo to the framebuffer. - * - * Returns 0 on success, -1 on failure. - */ -static int render_logo_to_framebuffer(uintptr_t framebuffer_base, uint32_t bytes_per_scanline, - uint32_t horizontal_resolution, uint32_t vertical_resolution, - struct soc_intel_common_config *config, enum bootsplash_type logo_type) -{ - uintptr_t logo_ptr; - size_t logo_ptr_size; - efi_uintn_t blt_size, blt_buffer_addr; - uint32_t logo_height, logo_width; - struct logo_coordinates logo_coords; - - logo_ptr = (uintptr_t)bmp_load_logo_by_type(logo_type, &logo_ptr_size); - - if (!logo_ptr || logo_ptr_size < sizeof(efi_bmp_image_header)) { - printk(BIOS_ERR, "%s: BMP image (%zu) is less than expected minimum size (%zu).\n", - __func__, logo_ptr_size, sizeof(efi_bmp_image_header)); - return -1; - } - - fsp_convert_bmp_to_gop_blt(logo_ptr, logo_ptr_size, &blt_buffer_addr, &blt_size, - &logo_height, &logo_width, config->panel_orientation); - - enum fw_splash_horizontal_alignment halignment = FW_SPLASH_HALIGNMENT_CENTER; - enum fw_splash_vertical_alignment valignment = FW_SPLASH_VALIGNMENT_CENTER; - /* Determine logo coordinates based on context (main logo vs. footer) */ - if (logo_type == BOOTSPLASH_LOW_BATTERY || logo_type == BOOTSPLASH_CENTER) { - /* Override logo alignment if the default screen orientation is not normal */ - if (config->panel_orientation != LB_FB_ORIENTATION_NORMAL) - config->logo_valignment = FW_SPLASH_VALIGNMENT_CENTER; - - valignment = config->logo_valignment; - } else { - /* Calculate logo destination coordinates */ - switch (config->panel_orientation) { - case LB_FB_ORIENTATION_RIGHT_UP: - halignment = FW_SPLASH_HALIGNMENT_LEFT; - break; - case LB_FB_ORIENTATION_LEFT_UP: - halignment = FW_SPLASH_HALIGNMENT_RIGHT; - break; - case LB_FB_ORIENTATION_BOTTOM_UP: - valignment = FW_SPLASH_VALIGNMENT_TOP; - break; - default: /* LB_FB_ORIENTATION_NORMAL (default) */ - valignment = FW_SPLASH_VALIGNMENT_BOTTOM; - break; - } - } - - logo_coords = calculate_logo_coordinates(horizontal_resolution, - vertical_resolution, logo_width, logo_height, valignment, halignment); - - /* Adjust the logo axis for rendering footer bootsplash if any override is available */ - if (logo_type == BOOTSPLASH_FOOTER && config->logo_bottom_margin) { - if (config->panel_orientation == LB_FB_ORIENTATION_BOTTOM_UP || - config->panel_orientation == LB_FB_ORIENTATION_NORMAL) - logo_coords.y -= config->logo_bottom_margin; - else - logo_coords.x -= config->logo_bottom_margin; - } - - copy_logo_to_framebuffer(framebuffer_base, bytes_per_scanline, blt_buffer_addr, - logo_width, logo_height, logo_coords.x, logo_coords.y); - - bmp_release_logo(); - - return 0; -} - void soc_load_logo_by_coreboot(void) { const struct hob_graphics_info *ginfo; struct soc_intel_common_config *config = chip_get_common_soc_structure(); int temp_mtrr_index = -1; + struct logo_config logo_cfg; size_t size; /* Find the graphics information HOB */ @@ -226,12 +64,6 @@ void soc_load_logo_by_coreboot(void) return; } - uintptr_t framebuffer_bar = ginfo->framebuffer_base; - uint32_t horizontal_resolution = ginfo->horizontal_resolution; - uint32_t vertical_resolution = ginfo->vertical_resolution; - uint32_t bytes_per_scanline = ginfo->pixels_per_scanline * - sizeof(efi_graphics_output_blt_pixel); - /* * Adjusts panel orientation for external display when the lid is closed. * @@ -243,33 +75,20 @@ void soc_load_logo_by_coreboot(void) if (CONFIG(VBOOT_LID_SWITCH) ? !get_lid_switch() : !CONFIG(RUN_FSP_GOP)) config->panel_orientation = LB_FB_ORIENTATION_NORMAL; - /* - * If the device is in low-battery mode and the low-battery splash screen is being - * displayed, prevent further operation and bail out early. - */ - if (platform_is_low_battery_shutdown_needed()) { - if (render_logo_to_framebuffer(framebuffer_bar, bytes_per_scanline, - horizontal_resolution, vertical_resolution, config, - BOOTSPLASH_LOW_BATTERY) != 0) { - printk(BIOS_ERR, "%s: Failed to render low-battery logo.\n", __func__); - } - goto cleanup; - } - /* Render the main logo */ - if (render_logo_to_framebuffer(framebuffer_bar, bytes_per_scanline, - horizontal_resolution, vertical_resolution, config, - BOOTSPLASH_CENTER) != 0) { - printk(BIOS_ERR, "%s: Failed to render main splash screen logo.\n", __func__); - } else if (CONFIG(SPLASH_SCREEN_FOOTER)) { - /* Render the footer logo */ - if (render_logo_to_framebuffer(framebuffer_bar, bytes_per_scanline, - horizontal_resolution, vertical_resolution, config, - BOOTSPLASH_FOOTER) != 0) - printk(BIOS_ERR, "%s: Failed to render footer logo.\n", __func__); - } + memset((void *)&logo_cfg, 0, sizeof(logo_cfg)); + logo_cfg.framebuffer_base = ginfo->framebuffer_base; + logo_cfg.horizontal_resolution = ginfo->horizontal_resolution; + logo_cfg.vertical_resolution = ginfo->vertical_resolution; + logo_cfg.bytes_per_scanline = ginfo->pixels_per_scanline * + sizeof(efi_graphics_output_blt_pixel); + logo_cfg.panel_orientation = config->panel_orientation; + logo_cfg.halignment = FW_SPLASH_HALIGNMENT_CENTER; + logo_cfg.valignment = config->logo_valignment; + logo_cfg.logo_bottom_margin = config->logo_bottom_margin; + + render_logo_to_framebuffer(&logo_cfg); -cleanup: /* Clear temporary Write Combine (WC) MTRR */ clear_var_mtrr(temp_mtrr_index); } diff --git a/src/drivers/intel/fsp2_0/fsp_gop_blt.c b/src/drivers/intel/fsp2_0/fsp_gop_blt.c index bc37ca6baa..52bc3c1989 100644 --- a/src/drivers/intel/fsp2_0/fsp_gop_blt.c +++ b/src/drivers/intel/fsp2_0/fsp_gop_blt.c @@ -7,450 +7,13 @@ #include #include -static bool is_bmp_image_valid(efi_bmp_image_header *header) -{ - /* Check if the BMP Header Signature is valid */ - if (header->CharB != 'B' || header->CharM != 'M') - return false; - - /* Check if the BMP Image Header Length is valid */ - if (!header->PixelHeight || !header->PixelWidth) - return false; - - if (header->Size < header->ImageOffset) - return false; - - if (header->ImageOffset < sizeof(efi_bmp_image_header)) - return false; - - return true; -} - -static bool is_bmp_image_compressed(efi_bmp_image_header *header) -{ - return header->CompressionType != 0; -} - -static bool is_bitmap_format_supported(efi_bmp_image_header *header) -{ - /* - * Check BITMAP format is supported - * BMP_IMAGE_HEADER = BITMAP_FILE_HEADER + BITMAP_INFO_HEADER - */ - if (header->HeaderSize != sizeof(efi_bmp_image_header) - - OFFSET_OF(efi_bmp_image_header, HeaderSize)) - return false; - - return true; -} - -static bool do_bmp_image_authentication(efi_bmp_image_header *header) -{ - if (!is_bmp_image_valid(header)) { - printk(BIOS_ERR, "%s: BMP Image Header is invalid.\n", __func__); - return false; - } - - /* - * BMP image compression is unsupported by FSP implementation, - * hence, exit if the BMP image is compressed. - */ - if (is_bmp_image_compressed(header)) { - printk(BIOS_ERR, "%s: BMP Image Compression is unsupported.\n", __func__); - return false; - } - - if (!is_bitmap_format_supported(header)) { - printk(BIOS_ERR, "%s: BmpHeader Header Size (0x%x) is not as expected.\n", - __func__, header->HeaderSize); - return false; - } - - return true; -} - -static uint32_t calculate_blt_buffer_size(efi_bmp_image_header *header) -{ - uint32_t blt_buffer_size; - - /* Calculate the size required for BLT buffer */ - blt_buffer_size = header->PixelWidth * header->PixelHeight * - sizeof(efi_graphics_output_blt_pixel); - if (!blt_buffer_size) - return 0; - - return blt_buffer_size; -} - -static int get_color_map_num(efi_bmp_image_header *header) -{ - int col_map_number; - - switch (header->BitPerPixel) { - case 1: - col_map_number = 2; - break; - case 4: - col_map_number = 16; - break; - case 8: - col_map_number = 256; - break; - default: - /* - * For other bit depths (e.g., 24-bit and 32-bit) that doesn't have - * a standard palette, col_map_number remains 0. - */ - col_map_number = 0; - break; - } - - /* - * At times BMP file may have padding data between its header section and the - * data section. - */ - if (header->ImageOffset - sizeof(efi_bmp_image_header) < - sizeof(efi_bmp_color_map) * col_map_number) - return -1; - - return col_map_number; -} - -/* - * Visual Representation of the Flipping: - * - * Original BMP Image: - * - * 0 -----------------------------------------PixelWidth-1 - * | | - * | | - * | | - * PixelHeight-1------------------------------| - * - * Blitting Region: - * - * (x, y) ----------------------------------- (x + blt_width, y) - * | | - * | | - * (x, y + blt_height) -------------------- --(x + blt_width, y + blt_height) - * - * Flipped Coordinates: - * - * flipped_x: Represents the horizontal coordinate from the `right` edge of the BMP, - * accounting for the blit width. - * - * flipped_y: Represents the vertical coordinate from the `bottom` edge of the BMP, - * accounting for the blit height. - * - * Example (Orientation: LB_FB_ORIENTATION_BOTTOM_UP): - * - * If blt_width is small, flipped_x will be near PixelWidth. - * If blt_height is small, flipped_y will be near PixelHeight. - * - * The blit then starts at (flipped_x, blt_height), effectively flipping the - * horizontal position and using the original blit height. - * - * This allows for blitting from the bottom-left corner of the display panel. - */ -static bool calculate_adj_height_width(const efi_bmp_image_header *header, size_t *adjusted_x, - size_t *adjusted_y, enum lb_fb_orientation orientation, int blt_width, int blt_height) -{ - size_t flipped_x = header->PixelWidth - blt_width - 1; - size_t flipped_y = header->PixelHeight - blt_height - 1; - - switch (orientation) { - case LB_FB_ORIENTATION_LEFT_UP: - *adjusted_x = flipped_y; - *adjusted_y = flipped_x; - break; - - case LB_FB_ORIENTATION_BOTTOM_UP: - *adjusted_x = flipped_x; - *adjusted_y = blt_height; - break; - - case LB_FB_ORIENTATION_RIGHT_UP: - *adjusted_x = blt_height; - *adjusted_y = blt_width; - break; - - case LB_FB_ORIENTATION_NORMAL: - default: - *adjusted_x = blt_width; - *adjusted_y = flipped_y; - break; - } - - return true; -} - -/* - * Visual Representation of gop_width and calculate linear offset into the GOP buffer: - * - * GOP Display Buffer: - * - * +------------------------------------------------+ - * | 0 | - * | |--> gop_width pixels in this row | - * +------------------------------------------------+ - * | gop_width | - * | | - * | gop_width | - * | ... | - * +------------------------------------------------+ - * | gop_width | - * +------------------------------------------------+ - * - * gop_width: Represents the width of a row in the GOP buffer. - * - It is determined by the `orientation` of the image. - * - For `LEFT_UP` and `RIGHT_UP`, the GOP width is the BMP image's `PixelHeight`. - * - For `BOTTOM_UP` and `NORMAL`, the GOP width is the BMP image's `PixelWidth`. - * - * gop_x, gop_y: Coordinates within the GOP buffer where the blit will start. - * - These are calculated by `calculate_adj_height_width` based on the `orientation`, - * `blt_width`, and `blt_height`. - * - * Pointer to the calculated pixel in the GOP blit buffer, or NULL on error. - * - Calculate `gop_blt_offset`as `gop_y * gop_width + gop_x` to determine the offset - * to access the correct pixel within `gop_blt_buffer`. - * - `gop_y * gop_width` calculates the offset of the row. - * - `gop_x` calculates the offset within that row. - */ -static efi_graphics_output_blt_pixel *get_gop_blt_pixel( - efi_graphics_output_blt_pixel *gop_blt_buffer, const efi_bmp_image_header *header, - int blt_width, int blt_height, enum lb_fb_orientation orientation) -{ - size_t gop_x; - size_t gop_y; - size_t gop_width; - - switch (orientation) { - case LB_FB_ORIENTATION_LEFT_UP: - case LB_FB_ORIENTATION_RIGHT_UP: - gop_width = header->PixelHeight; - break; - - case LB_FB_ORIENTATION_BOTTOM_UP: - case LB_FB_ORIENTATION_NORMAL: - default: - gop_width = header->PixelWidth; - break; - } - - if (!calculate_adj_height_width(header, &gop_x, &gop_y, orientation, - blt_width, blt_height)) - return NULL; - - /* - * Calculated pixel in the Graphics Output Protocol (GOP) blit buffer. - * This offset represents the starting position where the blitted region will be placed - * in the framebuffer. - */ - return &gop_blt_buffer[gop_y * gop_width + gop_x]; -} - -/* - * Fill BMP image into BLT buffer format with optional orientation - * - * +------------------------------------------------------------------+ - * | BMP Image (header: efi_bmp_image_header) | - * | (PixelWidth, PixelHeight) | - * +------------------------------------------------------------------+ - * | - * | (blt_width, blt_height) : Region to blit - * V - * +------------------------------------------------------------------+ - * | calculate_adj_height_width(header, adjusted_x, adjusted_y, | - * | orientation, blt_width, blt_height)| - * +------------------------------------------------------------------+ - * | - * | Calculates adjusted coordinates based on orientation: - * | - flipped_x = header->PixelWidth - blt_width - 1 - * | - flipped_y = header->PixelHeight - blt_height - 1 - * | - Depending on 'orientation', adjusted_x and adjusted_y are set. - * V - * +-------------------------------------------------------------------+ - * | get_gop_blt_pixel(gop_blt_buffer, header, blt_width, blt_height, | - * | orientation) | - * +-------------------------------------------------------------------+ - * | - * | 1. Determines gop_width based on orientation: - * | - LEFT_UP/RIGHT_UP: gop_width = header->PixelHeight, - * | - BOTTOM_UP/NORMAL: gop_width = header->PixelWidth, - * | 2. Calls calculate_adj_height_width to get gop_x and gop_y. - * | 3. GOP BLT offset: calculates linear offset into the GOP buffer - * | - gop_blt_offset = gop_y * gop_width + gop_x - * | 4. GOP BLT offset is used to access the correct regions in: - * V - * +------------------------------------------------------------------+ - * | GOP Blit Buffer (Framebuffer) | - * +------------------------------------------------------------------+ - */ -static void *fill_blt_buffer(efi_bmp_image_header *header, - uintptr_t logo_ptr, size_t blt_buffer_size, enum lb_fb_orientation orientation) -{ - efi_graphics_output_blt_pixel *gop_blt_buffer; - efi_graphics_output_blt_pixel *gop_blt_ptr; - efi_graphics_output_blt_pixel *gop_blt; - uint8_t *bmp_image; - uint8_t *bmp_image_header; - efi_bmp_color_map *bmp_color_map; - size_t image_index; - - gop_blt_ptr = malloc(sizeof(blt_buffer_size)); - if (!gop_blt_ptr) - die("%s: out of memory. Consider increasing the `CONFIG_HEAP_SIZE`\n", - __func__); - - bmp_image = ((uint8_t *)logo_ptr) + header->ImageOffset; - bmp_image_header = bmp_image; - gop_blt_buffer = gop_blt_ptr; - bmp_color_map = (efi_bmp_color_map *)(logo_ptr + sizeof(efi_bmp_image_header)); - - for (size_t height = 0; height < header->PixelHeight; height++) { - for (size_t width = 0; width < header->PixelWidth; width++, bmp_image++) { - size_t index = 0; - - gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, - orientation); - if (!gop_blt) { - free(gop_blt_ptr); - return NULL; - } - - switch (header->BitPerPixel) { - /* Translate 1-bit (2 colors) BMP to 24-bit color */ - case 1: - for (index = 0; index < 8 && width < header->PixelWidth; index++) { - uint8_t bit = ((*bmp_image) >> (7 - index)) & 0x1; - gop_blt->Red = bmp_color_map[bit].Red; - gop_blt->Green = bmp_color_map[bit].Green; - gop_blt->Blue = bmp_color_map[bit].Blue; - width++; - gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, - orientation); - if (!gop_blt) { - free(gop_blt_ptr); - return NULL; - } - } - width--; - break; - - /* Translate 4-bit (16 colors) BMP Palette to 24-bit color */ - case 4: - index = (*bmp_image) >> 4; - gop_blt->Red = bmp_color_map[index].Red; - gop_blt->Green = bmp_color_map[index].Green; - gop_blt->Blue = bmp_color_map[index].Blue; - if (width < (header->PixelWidth - 1)) { - width++; - gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, - orientation); - if (!gop_blt) { - free(gop_blt_ptr); - return NULL; - } - index = (*bmp_image) & 0x0f; - gop_blt->Red = bmp_color_map[index].Red; - gop_blt->Green = bmp_color_map[index].Green; - gop_blt->Blue = bmp_color_map[index].Blue; - } - break; - - /* Translate 8-bit (256 colors) BMP Palette to 24-bit color */ - case 8: - gop_blt->Red = bmp_color_map[*bmp_image].Red; - gop_blt->Green = bmp_color_map[*bmp_image].Green; - gop_blt->Blue = bmp_color_map[*bmp_image].Blue; - break; - - /* For 24-bit BMP */ - case 24: - gop_blt->Blue = *bmp_image++; - gop_blt->Green = *bmp_image++; - gop_blt->Red = *bmp_image; - break; - - /* Convert 32 bit to 24bit bmp - just ignore the final byte of each pixel */ - case 32: - gop_blt->Blue = *bmp_image++; - gop_blt->Green = *bmp_image++; - gop_blt->Red = *bmp_image++; - break; - - /* Other bit format of BMP is not supported. */ - default: - free(gop_blt_ptr); - gop_blt_ptr = NULL; - - printk(BIOS_ERR, "%s, BMP Bit format not supported. 0x%X\n", __func__, - header->BitPerPixel); - return NULL; - } - } - image_index = (uintptr_t)bmp_image - (uintptr_t)bmp_image_header; - /* Each row in BMP Image should be 4-byte align */ - if ((image_index % 4) != 0) - bmp_image = bmp_image + (4 - (image_index % 4)); - } - - return gop_blt_ptr; -} - -/* Helper function to perform the common BMP to GOP BLT conversion logic */ -static bool convert_bmp_to_gop_blt_common(uintptr_t logo_ptr, size_t logo_ptr_size, - efi_uintn_t *blt_ptr, efi_uintn_t *blt_size, uint32_t *pixel_height, - uint32_t *pixel_width, enum lb_fb_orientation orientation) -{ - size_t blt_buffer_size; - efi_bmp_image_header *bmp_header; - - bmp_header = (efi_bmp_image_header *)logo_ptr; - - /* Authenticate BMP header and validate size against provided logo_ptr_size */ - if (!do_bmp_image_authentication(bmp_header) || (bmp_header->Size != logo_ptr_size)) - return false; - - blt_buffer_size = calculate_blt_buffer_size(bmp_header); - if (!blt_buffer_size) - return false; - - if (get_color_map_num(bmp_header) < 0) - return false; - - bool is_standard_orientation = (orientation == LB_FB_ORIENTATION_NORMAL || - orientation == LB_FB_ORIENTATION_BOTTOM_UP); - - *blt_size = blt_buffer_size; - *pixel_height = is_standard_orientation ? bmp_header->PixelHeight : bmp_header->PixelWidth; - *pixel_width = is_standard_orientation ? bmp_header->PixelWidth : bmp_header->PixelHeight; - *blt_ptr = (uintptr_t)fill_blt_buffer(bmp_header, logo_ptr, blt_buffer_size, orientation); - - return true; -} - /* Convert a *.BMP graphics image to a GOP blt buffer */ void fsp_load_and_convert_bmp_to_gop_blt(efi_uintn_t *logo, uint32_t *logo_size, efi_uintn_t *blt_ptr, efi_uintn_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, enum lb_fb_orientation orientation) { - uintptr_t logo_ptr; - size_t logo_ptr_size; - - if (!logo || !logo_size || !blt_ptr || !blt_size || !pixel_height || !pixel_width) - return; - - logo_ptr = (uintptr_t)bmp_load_logo(&logo_ptr_size); - - if (!logo_ptr || logo_ptr_size < sizeof(efi_bmp_image_header)) - return; - - *logo = logo_ptr; - *logo_size = logo_ptr_size; - - convert_bmp_to_gop_blt_common(logo_ptr, logo_ptr_size, blt_ptr, blt_size, - pixel_height, pixel_width, orientation); + load_and_convert_bmp_to_blt((uintptr_t *)logo, (size_t *)logo_size, (uintptr_t *)blt_ptr, + (size_t *)blt_size, pixel_height, pixel_width, orientation); } /* Convert a *.BMP graphics image (as per input `logo_ptr`) to a GOP blt buffer */ @@ -458,9 +21,6 @@ void fsp_convert_bmp_to_gop_blt(uintptr_t logo_ptr, size_t logo_ptr_size, efi_uintn_t *blt_ptr, efi_uintn_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, enum lb_fb_orientation orientation) { - if (!blt_ptr || !blt_size || !pixel_height || !pixel_width) - return; - - convert_bmp_to_gop_blt_common(logo_ptr, logo_ptr_size, blt_ptr, blt_size, - pixel_height, pixel_width, orientation); + convert_bmp_to_blt(logo_ptr, logo_ptr_size, (uintptr_t *)blt_ptr, (size_t *)blt_size, + pixel_height, pixel_width, orientation); } diff --git a/src/drivers/intel/fsp2_0/silicon_init.c b/src/drivers/intel/fsp2_0/silicon_init.c index f6ed00be4d..6de71c9202 100644 --- a/src/drivers/intel/fsp2_0/silicon_init.c +++ b/src/drivers/intel/fsp2_0/silicon_init.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -300,11 +299,3 @@ void fsp_silicon_init(void) } __weak void soc_load_logo_by_fsp(FSPS_UPD *supd) { } - -static void release_logo(void *arg_unused) -{ - if (CONFIG(BMP_LOGO)) - bmp_release_logo(); -} - -BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, release_logo, NULL); diff --git a/src/include/bootsplash.h b/src/include/bootsplash.h index a8e51cf1ba..841dd50b7b 100644 --- a/src/include/bootsplash.h +++ b/src/include/bootsplash.h @@ -3,6 +3,7 @@ #ifndef __BOOTSPLASH_H__ #define __BOOTSPLASH_H__ +#include #include enum bootsplash_type { @@ -27,6 +28,125 @@ void set_bootsplash(unsigned char *framebuffer, unsigned int x_resolution, unsigned int y_resolution, unsigned int bytes_per_line, unsigned int fb_resolution); +/* + * Specifies the vertical alignment for the splash screen image. + * + * Visual Guide (representing the display area and the [LOGO]): + * + * Each option dictates the vertical placement of the splash image + * within the display's height. + */ +enum fw_splash_vertical_alignment { + /* + * The splash image is centered vertically `(Y-axis - logo_height)/2` on the screen. + * The center of the [LOGO] aligns with the vertical center of the screen. + * + * +---------------+ + * | | + * | | + * | [LOGO] | <-- Vertically Centered + * | | + * | | + * +---------------+ + */ + FW_SPLASH_VALIGNMENT_CENTER = 0, + + /* + * The splash image is aligned to the top edge of the screen. + * + * +---------------+ + * | [LOGO] | <-- Top Aligned + * | | + * | | + * | | + * | | + * +---------------+ + */ + FW_SPLASH_VALIGNMENT_TOP = 1, + + /* + * The splash image is aligned to the bottom edge of the screen. + * + * +---------------+ + * | | + * | | + * | | + * | | + * | [LOGO] | <-- Bottom Aligned + * +---------------+ + */ + FW_SPLASH_VALIGNMENT_BOTTOM = 2, + + /* + * The splash image is placed in the vertical middle `(Y-axis/2)` of the screen + * (without considering the `logo height`). This means the TOP EDGE of the + * [LOGO] aligns with the screen's vertical midpoint line. + * + * +---------------+ + * | (Upper Half) | + * | | + * | [LOGO] | <-- [LOGO] aligns at Middle of the Y-axis + * | | + * | (Lower Half) | + * +---------------+ + * + * Note: The distinction between CENTER and MIDDLE is relevant as in for MIDDLE + * alignment, it ignores the logo height (i.e., the logo's top edge is placed + * at the screen's Y-midpoint). CENTER alignment, by contrast, would place + * the geometrical center of the logo at the screen's Y-midpoint. + */ + FW_SPLASH_VALIGNMENT_MIDDLE = 3, +}; + +enum fw_splash_horizontal_alignment { + /* + * The splash image is centered horizontally `(X-axis - logo_width)/2` on the screen. + * The center of the [LOGO] aligns with the horizontal center of the screen. + * + * +-----------------+ + * | [LOGO] | + * +-----------------+ + */ + FW_SPLASH_HALIGNMENT_CENTER = 0, + + /* + * The splash image is aligned to the left edge of the screen. + * + * +-----------------+ + * |[LOGO] | + * +-----------------+ + */ + FW_SPLASH_HALIGNMENT_LEFT = 1, + + /* + * The splash image is aligned to the right edge of the screen. + * + * +-----------------+ + * | [LOGO]| + * +-----------------+ + */ + FW_SPLASH_HALIGNMENT_RIGHT = 2, +}; + +struct logo_config { + uintptr_t framebuffer_base; + uint32_t horizontal_resolution; + uint32_t vertical_resolution; + uint32_t bytes_per_scanline; + enum lb_fb_orientation panel_orientation; + enum fw_splash_horizontal_alignment halignment; + enum fw_splash_vertical_alignment valignment; + uint8_t logo_bottom_margin; +}; + +void render_logo_to_framebuffer(struct logo_config *config); +void load_and_convert_bmp_to_blt(uintptr_t *logo, size_t *logo_size, + uintptr_t *blt, size_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, + enum lb_fb_orientation orientation); +void convert_bmp_to_blt(uintptr_t logo, size_t logo_size, + uintptr_t *blt, size_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, + enum lb_fb_orientation orientation); + /* * Allow platform-specific BMP logo overrides via HAVE_CUSTOM_BMP_LOGO config. * For example: Introduce configurable BMP logo for customization on platforms like ChromeOS diff --git a/src/lib/Makefile.mk b/src/lib/Makefile.mk index f13bb66f79..ecb06c4765 100644 --- a/src/lib/Makefile.mk +++ b/src/lib/Makefile.mk @@ -148,6 +148,7 @@ ramstage-y += hexstrtobin.c ramstage-y += wrdd.c ramstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c ramstage-$(CONFIG_BMP_LOGO) += bmp_logo.c +ramstage-$(CONFIG_BMP_LOGO) += render_bmp.c ramstage-$(CONFIG_BOOTSPLASH) += bootsplash.c ramstage-$(CONFIG_BOOTSPLASH) += jpeg.c ramstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c diff --git a/src/lib/render_bmp.c b/src/lib/render_bmp.c new file mode 100644 index 0000000000..7dc808b13c --- /dev/null +++ b/src/lib/render_bmp.c @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include + +#include "render_bmp.h" + +static bool is_bmp_image_valid(struct bmp_image_header *header) +{ + /* Check if the BMP Header Signature is valid */ + if (header->CharB != 'B' || header->CharM != 'M') + return false; + + /* Check if the BMP Image Header Length is valid */ + if (!header->PixelHeight || !header->PixelWidth) + return false; + + if (header->Size < header->ImageOffset) + return false; + + if (header->ImageOffset < sizeof(struct bmp_image_header)) + return false; + + return true; +} + +static bool is_bmp_image_compressed(struct bmp_image_header *header) +{ + return header->CompressionType != 0; +} + +static bool is_bitmap_format_supported(struct bmp_image_header *header) +{ + /* + * Check BITMAP format is supported + * BMP_IMAGE_HEADER = BITMAP_FILE_HEADER + BITMAP_INFO_HEADER + */ + if (header->HeaderSize != sizeof(struct bmp_image_header) - + OFFSET_OF(struct bmp_image_header, HeaderSize)) + return false; + + return true; +} + +static bool do_bmp_image_authentication(struct bmp_image_header *header) +{ + if (!is_bmp_image_valid(header)) { + printk(BIOS_ERR, "%s: BMP Image Header is invalid.\n", __func__); + return false; + } + + /* + * BMP image compression is unsupported by FSP implementation, + * hence, exit if the BMP image is compressed. + */ + if (is_bmp_image_compressed(header)) { + printk(BIOS_ERR, "%s: BMP Image Compression is unsupported.\n", __func__); + return false; + } + + if (!is_bitmap_format_supported(header)) { + printk(BIOS_ERR, "%s: BmpHeader Header Size (0x%x) is not as expected.\n", + __func__, header->HeaderSize); + return false; + } + + return true; +} + +static uint32_t calculate_blt_buffer_size(struct bmp_image_header *header) +{ + uint32_t blt_buffer_size; + + /* Calculate the size required for BLT buffer */ + blt_buffer_size = header->PixelWidth * header->PixelHeight * + sizeof(struct blt_pixel); + if (!blt_buffer_size) + return 0; + + return blt_buffer_size; +} + +static int get_color_map_num(struct bmp_image_header *header) +{ + int col_map_number; + + switch (header->BitPerPixel) { + case 1: + col_map_number = 2; + break; + case 4: + col_map_number = 16; + break; + case 8: + col_map_number = 256; + break; + default: + /* + * For other bit depths (e.g., 24-bit and 32-bit) that doesn't have + * a standard palette, col_map_number remains 0. + */ + col_map_number = 0; + break; + } + + /* + * At times BMP file may have padding data between its header section and the + * data section. + */ + if (header->ImageOffset - sizeof(struct bmp_image_header) < + sizeof(struct bmp_color_map) * col_map_number) + return -1; + + return col_map_number; +} + +/* + * Visual Representation of the Flipping: + * + * Original BMP Image: + * + * 0 -----------------------------------------PixelWidth-1 + * | | + * | | + * | | + * PixelHeight-1------------------------------| + * + * Blitting Region: + * + * (x, y) ----------------------------------- (x + blt_width, y) + * | | + * | | + * (x, y + blt_height) -------------------- --(x + blt_width, y + blt_height) + * + * Flipped Coordinates: + * + * flipped_x: Represents the horizontal coordinate from the `right` edge of the BMP, + * accounting for the blit width. + * + * flipped_y: Represents the vertical coordinate from the `bottom` edge of the BMP, + * accounting for the blit height. + * + * Example (Orientation: LB_FB_ORIENTATION_BOTTOM_UP): + * + * If blt_width is small, flipped_x will be near PixelWidth. + * If blt_height is small, flipped_y will be near PixelHeight. + * + * The blit then starts at (flipped_x, blt_height), effectively flipping the + * horizontal position and using the original blit height. + * + * This allows for blitting from the bottom-left corner of the display panel. + */ +static bool calculate_adj_height_width(const struct bmp_image_header *header, size_t *adjusted_x, + size_t *adjusted_y, enum lb_fb_orientation orientation, int blt_width, int blt_height) +{ + size_t flipped_x = header->PixelWidth - blt_width - 1; + size_t flipped_y = header->PixelHeight - blt_height - 1; + + switch (orientation) { + case LB_FB_ORIENTATION_LEFT_UP: + *adjusted_x = flipped_y; + *adjusted_y = flipped_x; + break; + + case LB_FB_ORIENTATION_BOTTOM_UP: + *adjusted_x = flipped_x; + *adjusted_y = blt_height; + break; + + case LB_FB_ORIENTATION_RIGHT_UP: + *adjusted_x = blt_height; + *adjusted_y = blt_width; + break; + + case LB_FB_ORIENTATION_NORMAL: + default: + *adjusted_x = blt_width; + *adjusted_y = flipped_y; + break; + } + + return true; +} + +/* + * Visual Representation of gop_width and calculate linear offset into the GOP buffer: + * + * GOP Display Buffer: + * + * +------------------------------------------------+ + * | 0 | + * | |--> gop_width pixels in this row | + * +------------------------------------------------+ + * | gop_width | + * | | + * | gop_width | + * | ... | + * +------------------------------------------------+ + * | gop_width | + * +------------------------------------------------+ + * + * gop_width: Represents the width of a row in the GOP buffer. + * - It is determined by the `orientation` of the image. + * - For `LEFT_UP` and `RIGHT_UP`, the GOP width is the BMP image's `PixelHeight`. + * - For `BOTTOM_UP` and `NORMAL`, the GOP width is the BMP image's `PixelWidth`. + * + * gop_x, gop_y: Coordinates within the GOP buffer where the blit will start. + * - These are calculated by `calculate_adj_height_width` based on the `orientation`, + * `blt_width`, and `blt_height`. + * + * Pointer to the calculated pixel in the GOP blit buffer, or NULL on error. + * - Calculate `gop_blt_offset`as `gop_y * gop_width + gop_x` to determine the offset + * to access the correct pixel within `gop_blt_buffer`. + * - `gop_y * gop_width` calculates the offset of the row. + * - `gop_x` calculates the offset within that row. + */ +static struct blt_pixel *get_gop_blt_pixel( + struct blt_pixel *gop_blt_buffer, const struct bmp_image_header *header, + int blt_width, int blt_height, enum lb_fb_orientation orientation) +{ + size_t gop_x; + size_t gop_y; + size_t gop_width; + + switch (orientation) { + case LB_FB_ORIENTATION_LEFT_UP: + case LB_FB_ORIENTATION_RIGHT_UP: + gop_width = header->PixelHeight; + break; + + case LB_FB_ORIENTATION_BOTTOM_UP: + case LB_FB_ORIENTATION_NORMAL: + default: + gop_width = header->PixelWidth; + break; + } + + if (!calculate_adj_height_width(header, &gop_x, &gop_y, orientation, + blt_width, blt_height)) + return NULL; + + /* + * Calculated pixel in the Graphics Output Protocol (GOP) blit buffer. + * This offset represents the starting position where the blitted region will be placed + * in the framebuffer. + */ + return &gop_blt_buffer[gop_y * gop_width + gop_x]; +} + +/* + * Fill BMP image into BLT buffer format with optional orientation + * + * +------------------------------------------------------------------+ + * | BMP Image (header: bmp_image_header) | + * | (PixelWidth, PixelHeight) | + * +------------------------------------------------------------------+ + * | + * | (blt_width, blt_height) : Region to blit + * V + * +------------------------------------------------------------------+ + * | calculate_adj_height_width(header, adjusted_x, adjusted_y, | + * | orientation, blt_width, blt_height)| + * +------------------------------------------------------------------+ + * | + * | Calculates adjusted coordinates based on orientation: + * | - flipped_x = header->PixelWidth - blt_width - 1 + * | - flipped_y = header->PixelHeight - blt_height - 1 + * | - Depending on 'orientation', adjusted_x and adjusted_y are set. + * V + * +-------------------------------------------------------------------+ + * | get_gop_blt_pixel(gop_blt_buffer, header, blt_width, blt_height, | + * | orientation) | + * +-------------------------------------------------------------------+ + * | + * | 1. Determines gop_width based on orientation: + * | - LEFT_UP/RIGHT_UP: gop_width = header->PixelHeight, + * | - BOTTOM_UP/NORMAL: gop_width = header->PixelWidth, + * | 2. Calls calculate_adj_height_width to get gop_x and gop_y. + * | 3. GOP BLT offset: calculates linear offset into the GOP buffer + * | - gop_blt_offset = gop_y * gop_width + gop_x + * | 4. GOP BLT offset is used to access the correct regions in: + * V + * +------------------------------------------------------------------+ + * | GOP Blit Buffer (Framebuffer) | + * +------------------------------------------------------------------+ + */ +static void *fill_blt_buffer(struct bmp_image_header *header, + uintptr_t logo, size_t blt_buffer_size, enum lb_fb_orientation orientation) +{ + struct blt_pixel *gop_blt_buffer; + struct blt_pixel *gop_blt_ptr; + struct blt_pixel *gop_blt; + uint8_t *bmp_image; + uint8_t *bmp_header; + struct bmp_color_map *color_map; + size_t image_index; + + gop_blt_ptr = malloc(sizeof(blt_buffer_size)); + if (!gop_blt_ptr) + die("%s: out of memory. Consider increasing the `CONFIG_HEAP_SIZE`\n", + __func__); + + bmp_image = ((uint8_t *)logo) + header->ImageOffset; + bmp_header = bmp_image; + gop_blt_buffer = gop_blt_ptr; + color_map = (struct bmp_color_map *)(logo + sizeof(struct bmp_image_header)); + + for (size_t height = 0; height < header->PixelHeight; height++) { + for (size_t width = 0; width < header->PixelWidth; width++, bmp_image++) { + size_t index = 0; + + gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, + orientation); + if (!gop_blt) { + free(gop_blt_ptr); + return NULL; + } + + switch (header->BitPerPixel) { + /* Translate 1-bit (2 colors) BMP to 24-bit color */ + case 1: + for (index = 0; index < 8 && width < header->PixelWidth; index++) { + uint8_t bit = ((*bmp_image) >> (7 - index)) & 0x1; + gop_blt->Red = color_map[bit].Red; + gop_blt->Green = color_map[bit].Green; + gop_blt->Blue = color_map[bit].Blue; + width++; + gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, + orientation); + if (!gop_blt) { + free(gop_blt_ptr); + return NULL; + } + } + width--; + break; + + /* Translate 4-bit (16 colors) BMP Palette to 24-bit color */ + case 4: + index = (*bmp_image) >> 4; + gop_blt->Red = color_map[index].Red; + gop_blt->Green = color_map[index].Green; + gop_blt->Blue = color_map[index].Blue; + if (width < (header->PixelWidth - 1)) { + width++; + gop_blt = get_gop_blt_pixel(gop_blt_buffer, header, width, height, + orientation); + if (!gop_blt) { + free(gop_blt_ptr); + return NULL; + } + index = (*bmp_image) & 0x0f; + gop_blt->Red = color_map[index].Red; + gop_blt->Green = color_map[index].Green; + gop_blt->Blue = color_map[index].Blue; + } + break; + + /* Translate 8-bit (256 colors) BMP Palette to 24-bit color */ + case 8: + gop_blt->Red = color_map[*bmp_image].Red; + gop_blt->Green = color_map[*bmp_image].Green; + gop_blt->Blue = color_map[*bmp_image].Blue; + break; + + /* For 24-bit BMP */ + case 24: + gop_blt->Blue = *bmp_image++; + gop_blt->Green = *bmp_image++; + gop_blt->Red = *bmp_image; + break; + + /* Convert 32 bit to 24bit bmp - just ignore the final byte of each pixel */ + case 32: + gop_blt->Blue = *bmp_image++; + gop_blt->Green = *bmp_image++; + gop_blt->Red = *bmp_image++; + break; + + /* Other bit format of BMP is not supported. */ + default: + free(gop_blt_ptr); + gop_blt_ptr = NULL; + + printk(BIOS_ERR, "%s, BMP Bit format not supported. 0x%X\n", __func__, + header->BitPerPixel); + return NULL; + } + } + image_index = (uintptr_t)bmp_image - (uintptr_t)bmp_header; + /* Each row in BMP Image should be 4-byte align */ + if ((image_index % 4) != 0) + bmp_image = bmp_image + (4 - (image_index % 4)); + } + + return gop_blt_ptr; +} + +/* Helper function to perform the common BMP to GOP BLT conversion logic */ +static bool convert_bmp_to_gop_blt_common(uintptr_t logo, size_t logo_size, + uintptr_t *blt, size_t *blt_size, uint32_t *pixel_height, + uint32_t *pixel_width, enum lb_fb_orientation orientation) +{ + size_t blt_buffer_size; + struct bmp_image_header *bmp_header; + + bmp_header = (struct bmp_image_header *)logo; + + /* Authenticate BMP header and validate size against provided logo_size */ + if (!do_bmp_image_authentication(bmp_header) || (bmp_header->Size != logo_size)) + return false; + + blt_buffer_size = calculate_blt_buffer_size(bmp_header); + if (!blt_buffer_size) + return false; + + if (get_color_map_num(bmp_header) < 0) + return false; + + bool is_standard_orientation = (orientation == LB_FB_ORIENTATION_NORMAL || + orientation == LB_FB_ORIENTATION_BOTTOM_UP); + + *blt_size = blt_buffer_size; + *pixel_height = is_standard_orientation ? bmp_header->PixelHeight : bmp_header->PixelWidth; + *pixel_width = is_standard_orientation ? bmp_header->PixelWidth : bmp_header->PixelHeight; + *blt = (uintptr_t)fill_blt_buffer(bmp_header, logo, blt_buffer_size, orientation); + + return true; +} + +/* Convert a *.BMP graphics image to a blt buffer */ +void load_and_convert_bmp_to_blt(uintptr_t *logo, size_t *logo_size, + uintptr_t *blt, size_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, + enum lb_fb_orientation orientation) +{ + uintptr_t bmp; + size_t bmp_size; + + if (!logo || !logo_size || !blt || !blt_size || !pixel_height || !pixel_width) + return; + + bmp = (uintptr_t)bmp_load_logo(&bmp_size); + + if (!bmp || bmp_size < sizeof(struct bmp_image_header)) + return; + + *logo = bmp; + *logo_size = bmp_size; + + convert_bmp_to_gop_blt_common(*logo, *logo_size, blt, blt_size, + pixel_height, pixel_width, orientation); +} + +/* Convert a *.BMP graphics image (as per input `logo`) to a GOP blt buffer */ +void convert_bmp_to_blt(uintptr_t logo, size_t logo_size, + uintptr_t *blt, size_t *blt_size, uint32_t *pixel_height, uint32_t *pixel_width, + enum lb_fb_orientation orientation) +{ + if (!blt || !blt_size || !pixel_height || !pixel_width) + return; + + if (!logo || logo_size < sizeof(struct bmp_image_header)) + return; + + convert_bmp_to_gop_blt_common(logo, logo_size, blt, blt_size, + pixel_height, pixel_width, orientation); +} + +/* + * Calculates the destination coordinates for the logo based on both horizontal and + * vertical alignment settings. + * + * horizontal_resolution: The horizontal resolution of the display panel. + * vertical_resolution: The vertical resolution of the display panel. + * logo_width: The width of the logo bitmap. + * logo_height: The height of the logo bitmap. + * halignment: The horizontal alignment setting. Use FW_SPLASH_HALIGNMENT_NONE + * if only vertical alignment is specified. + * valignment: The vertical alignment setting. Use FW_SPLASH_VALIGNMENT_NONE + * if only horizontal alignment is specified. + * + * Returning `struct logo_coordinates` that contains the calculated x and y coordinates + * for rendering the logo. + */ +static struct logo_coordinates calculate_logo_coordinates( + uint32_t horizontal_resolution, uint32_t vertical_resolution, + uint32_t logo_width, uint32_t logo_height, + enum fw_splash_horizontal_alignment halignment, + enum fw_splash_vertical_alignment valignment) +{ + struct logo_coordinates coords; + + /* Calculate X coordinate */ + switch (halignment) { + case FW_SPLASH_HALIGNMENT_LEFT: + coords.x = 0; + break; + case FW_SPLASH_HALIGNMENT_RIGHT: + coords.x = horizontal_resolution - logo_width; + break; + default: /* FW_SPLASH_HALIGNMENT_CENTER (default) */ + coords.x = (horizontal_resolution - logo_width) / 2; + break; + } + + /* Calculate Y coordinate */ + switch (valignment) { + case FW_SPLASH_VALIGNMENT_MIDDLE: + coords.y = vertical_resolution / 2; + break; + case FW_SPLASH_VALIGNMENT_TOP: + coords.y = 0; + break; + case FW_SPLASH_VALIGNMENT_BOTTOM: + coords.y = vertical_resolution - logo_height; + break; + default: /* FW_SPLASH_VALIGNMENT_CENTER (default) */ + coords.y = (vertical_resolution - logo_height) / 2; + break; + } + + return coords; +} + +/* + * Copies the logo to the framebuffer. + * + * framebuffer_base: The base address of the framebuffer. + * bytes_per_scanline: The number of bytes per scanline in the framebuffer. + * logo_buffer: The address of the logo data in BLT format. + * logo_width: The width of the logo bitmap. + * logo_height: The height of the logo bitmap. + * dest_x: The destination x-coordinate in the framebuffer for rendering the logo. + * dest_y: The destination y-coordinate in the framebuffer for rendering the logo. + */ +static void copy_logo_to_framebuffer( + uintptr_t framebuffer_base, uint32_t bytes_per_scanline, + uintptr_t logo_buffer, uint32_t logo_width, uint32_t logo_height, + uint32_t dest_x, uint32_t dest_y) +{ + size_t pixel_size = sizeof(struct blt_pixel); + size_t bytes_per_logo_line = logo_width * pixel_size; + uint8_t *framebuffer_offset = (uint8_t *)framebuffer_base + dest_y * bytes_per_scanline + + dest_x * pixel_size; + uint8_t *dst_row_address = framebuffer_offset; + uint8_t *src_row_address = (uint8_t *)logo_buffer; + for (uint32_t i = 0; i < logo_height; i++) { + memcpy(dst_row_address, src_row_address, bytes_per_logo_line); + dst_row_address += bytes_per_scanline; + src_row_address += bytes_per_logo_line; + } +} + +/* + * Adjust logo layout based on the panel orientation. + * + * logo_type: Logo type. + * config: Logo configuration information. + * logo_halignment: Resultant horizontal alignment setting. + * logo_valignment: Resultant vertical alignment setting. + * logo_bottom_margin: Resultant bottom margin. + */ +static void get_logo_layout( + enum bootsplash_type logo_type, + struct logo_config *config, + enum fw_splash_horizontal_alignment *logo_halignment, + enum fw_splash_vertical_alignment *logo_valignment, + uint8_t *logo_bottom_margin +) +{ + if (!config || !logo_halignment || !logo_valignment || !logo_bottom_margin) + return; + + if (logo_type == BOOTSPLASH_LOW_BATTERY || logo_type == BOOTSPLASH_CENTER) { + *logo_halignment = config->halignment; + *logo_valignment = config->valignment; + /* Override logo alignment if the default screen orientation is not normal */ + if (config->panel_orientation != LB_FB_ORIENTATION_NORMAL) + *logo_valignment = FW_SPLASH_VALIGNMENT_CENTER; + *logo_bottom_margin = 0; + } else if (logo_type == BOOTSPLASH_FOOTER) { + *logo_halignment = FW_SPLASH_HALIGNMENT_CENTER; + *logo_valignment = FW_SPLASH_VALIGNMENT_CENTER; + switch (config->panel_orientation) { + case LB_FB_ORIENTATION_RIGHT_UP: + *logo_halignment = FW_SPLASH_HALIGNMENT_LEFT; + break; + case LB_FB_ORIENTATION_LEFT_UP: + *logo_halignment = FW_SPLASH_HALIGNMENT_RIGHT; + break; + case LB_FB_ORIENTATION_BOTTOM_UP: + *logo_valignment = FW_SPLASH_VALIGNMENT_TOP; + break; + default: /* LB_FB_ORIENTATION_NORMAL (default) */ + *logo_valignment = FW_SPLASH_VALIGNMENT_BOTTOM; + break; + } + *logo_bottom_margin = config->logo_bottom_margin; + } else { // Default values + *logo_halignment = FW_SPLASH_HALIGNMENT_CENTER; + *logo_valignment = FW_SPLASH_VALIGNMENT_CENTER; + *logo_bottom_margin = 0; + } +} + +/* + * Loads, converts, and renders a BMP logo to the framebuffer. + * + * logo_type: Logo type. + * config: Logo configuration information. + * + * Returns 0 on success, -1 on failure. + */ +static int load_and_render_logo_to_framebuffer( + enum bootsplash_type logo_type, + struct logo_config *config +) +{ + uintptr_t logo; + size_t logo_size; + size_t blt_size; + uintptr_t blt_buffer; + uint32_t logo_height, logo_width; + struct logo_coordinates logo_coords; + enum fw_splash_horizontal_alignment halignment; + enum fw_splash_vertical_alignment valignment; + uint8_t logo_bottom_margin; + + if (!config) + return -1; + + logo = (uintptr_t)bmp_load_logo_by_type(logo_type, &logo_size); + + if (!logo || logo_size < sizeof(struct bmp_image_header)) { + printk(BIOS_ERR, "%s: BMP image (%zu) is less than expected minimum size (%zu).\n", + __func__, logo_size, sizeof(struct bmp_image_header)); + return -1; + } + + convert_bmp_to_blt(logo, logo_size, &blt_buffer, &blt_size, + &logo_height, &logo_width, config->panel_orientation); + + get_logo_layout(logo_type, config, &halignment, &valignment, &logo_bottom_margin); + + logo_coords = calculate_logo_coordinates(config->horizontal_resolution, + config->vertical_resolution, logo_width, logo_height, halignment, valignment); + + if (logo_bottom_margin) { + if (config->panel_orientation == LB_FB_ORIENTATION_BOTTOM_UP || + config->panel_orientation == LB_FB_ORIENTATION_NORMAL) + logo_coords.y -= logo_bottom_margin; + else + logo_coords.x -= logo_bottom_margin; + } + + copy_logo_to_framebuffer(config->framebuffer_base, config->bytes_per_scanline, blt_buffer, + logo_width, logo_height, logo_coords.x, logo_coords.y); + + bmp_release_logo(); + + return 0; +} + +/* + * Loads, converts, and renders a BMP logo to the framebuffer. + * The logo type (primary or low battery etc.) is seletcted dynamically. + * + * config: Logo configuration information. + * + */ +void render_logo_to_framebuffer(struct logo_config *config) +{ + if (!config) + return; + + if (config->framebuffer_base == 0) { + /* Try to load from already populated framebuffer information */ + struct lb_framebuffer framebuffer; + memset(&framebuffer, 0, sizeof(struct lb_framebuffer)); + fill_lb_framebuffer(&framebuffer); + /* Exit if framebuffer is still not available */ + if (framebuffer.physical_address == 0) + return; + config->framebuffer_base = framebuffer.physical_address; + config->horizontal_resolution = framebuffer.x_resolution; + config->vertical_resolution = framebuffer.y_resolution; + config->bytes_per_scanline = framebuffer.bytes_per_line; + } + + /* + * If the device is in low-battery mode and the low-battery splash screen is being + * displayed, prevent further operation and bail out early. + */ + if (platform_is_low_battery_shutdown_needed()) { + if (load_and_render_logo_to_framebuffer(BOOTSPLASH_LOW_BATTERY, config) != 0) { + printk(BIOS_ERR, "%s: Failed to render low-battery logo.\n", __func__); + } + return; + } + + /* Render the main logo */ + if (load_and_render_logo_to_framebuffer(BOOTSPLASH_CENTER, config) != 0) { + printk(BIOS_ERR, "%s: Failed to render main splash screen logo.\n", __func__); + } else if (CONFIG(SPLASH_SCREEN_FOOTER)) { + /* Render the footer logo */ + if (load_and_render_logo_to_framebuffer(BOOTSPLASH_FOOTER, config) != 0) + printk(BIOS_ERR, "%s: Failed to render footer logo.\n", __func__); + } +} + +static void release_logo(void *arg_unused) +{ + bmp_release_logo(); +} + +BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, release_logo, NULL); diff --git a/src/lib/render_bmp.h b/src/lib/render_bmp.h new file mode 100644 index 0000000000..41ef273b86 --- /dev/null +++ b/src/lib/render_bmp.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __RENDER_BMP_H__ +#define __RENDER_BMP_H__ + +#ifndef OFFSET_OF +#if (defined (__GNUC__) && __GNUC__ >= 4) || defined (__clang__) +#define OFFSET_OF(TYPE, Field) ((size_t) __builtin_offsetof(TYPE, Field)) +#endif +#endif + +#ifndef OFFSET_OF +#define OFFSET_OF(TYPE, Field) ((size_t) &(((TYPE *)0)->Field)) +#endif + +#pragma pack(1) + +struct bmp_color_map { + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Reserved; +}; + +struct bmp_image_header { + char CharB; // 'B' + char CharM; // 'M' + uint32_t Size; + uint16_t Reserved[2]; + uint32_t ImageOffset; + uint32_t HeaderSize; + uint32_t PixelWidth; + uint32_t PixelHeight; + uint16_t Planes; + uint16_t BitPerPixel; // 1, 4, 8, 24 or 32 + uint32_t CompressionType; + uint32_t ImageSize; // Compressed image size in bytes + uint32_t XPixelsPerMeter; + uint32_t YPixelsPerMeter; + uint32_t NumberOfColors; + uint32_t ImportantColors; +}; + +struct blt_pixel { + uint8_t Blue; + uint8_t Green; + uint8_t Red; + uint8_t Reserved; +}; + +struct logo_coordinates { + uint32_t x; + uint32_t y; +}; + +#pragma pack() + +#endif // __RENDER_BMP_H__ diff --git a/src/soc/intel/common/block/include/intelblocks/cfg.h b/src/soc/intel/common/block/include/intelblocks/cfg.h index 8aac699525..26252ac867 100644 --- a/src/soc/intel/common/block/include/intelblocks/cfg.h +++ b/src/soc/intel/common/block/include/intelblocks/cfg.h @@ -4,6 +4,7 @@ #define SOC_INTEL_COMMON_BLOCK_CFG_H #include +#include #include #include #include @@ -13,106 +14,6 @@ enum { CHIPSET_LOCKDOWN_FSP, /* FSP handles locking per UPDs */ }; -/* - * Specifies the vertical alignment for the splash screen image. - * - * Visual Guide (representing the display area and the [LOGO]): - * - * Each option dictates the vertical placement of the splash image - * within the display's height. - */ -enum fw_splash_vertical_alignment { - /* - * The splash image is centered vertically `(Y-axis - logo_height)/2` on the screen. - * The center of the [LOGO] aligns with the vertical center of the screen. - * - * +---------------+ - * | | - * | | - * | [LOGO] | <-- Vertically Centered - * | | - * | | - * +---------------+ - */ - FW_SPLASH_VALIGNMENT_CENTER = 0, - - /* - * The splash image is aligned to the top edge of the screen. - * - * +---------------+ - * | [LOGO] | <-- Top Aligned - * | | - * | | - * | | - * | | - * +---------------+ - */ - FW_SPLASH_VALIGNMENT_TOP = 1, - - /* - * The splash image is aligned to the bottom edge of the screen. - * - * +---------------+ - * | | - * | | - * | | - * | | - * | [LOGO] | <-- Bottom Aligned - * +---------------+ - */ - FW_SPLASH_VALIGNMENT_BOTTOM = 2, - - /* - * The splash image is placed in the vertical middle `(Y-axis/2)` of the screen - * (without considering the `logo height`). This means the TOP EDGE of the - * [LOGO] aligns with the screen's vertical midpoint line. - * - * +---------------+ - * | (Upper Half) | - * | | - * | [LOGO] | <-- [LOGO] aligns at Middle of the Y-axis - * | | - * | (Lower Half) | - * +---------------+ - * - * Note: The distinction between CENTER and MIDDLE is relevant as in for MIDDLE - * alignment, it ignores the logo height (i.e., the logo's top edge is placed - * at the screen's Y-midpoint). CENTER alignment, by contrast, would place - * the geometrical center of the logo at the screen's Y-midpoint. - */ - FW_SPLASH_VALIGNMENT_MIDDLE = 3, -}; - -enum fw_splash_horizontal_alignment { - /* - * The splash image is centered horizontally `(X-axis - logo_width)/2` on the screen. - * The center of the [LOGO] aligns with the horizontal center of the screen. - * - * +-----------------+ - * | [LOGO] | - * +-----------------+ - */ - FW_SPLASH_HALIGNMENT_CENTER = 0, - - /* - * The splash image is aligned to the left edge of the screen. - * - * +-----------------+ - * |[LOGO] | - * +-----------------+ - */ - FW_SPLASH_HALIGNMENT_LEFT = 1, - - /* - * The splash image is aligned to the right edge of the screen. - * - * +-----------------+ - * | [LOGO]| - * +-----------------+ - */ - FW_SPLASH_HALIGNMENT_RIGHT = 2, -}; - /* * This structure will hold data required by common blocks. * These are soc specific configurations which will be filled by soc.