From 0e0a3ceb509b19af55c5064607d9057ec7d8c1c4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Feb 2018 22:14:22 +0100 Subject: [PATCH] efi_loader: implement missing bit blit operations in gop With the patch all block image transfer operations of the EFI_GRAPHICS_OUTPUT_PROTOCOL are supported. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_api.h | 10 ++- lib/efi_loader/efi_gop.c | 158 ++++++++++++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 37 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 433594f339..167c44e3a4 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -665,6 +665,13 @@ struct efi_gop_mode unsigned long fb_size; }; +struct efi_gop_pixel { + u8 blue; + u8 green; + u8 red; + u8 reserved; +}; + #define EFI_BLT_VIDEO_FILL 0 #define EFI_BLT_VIDEO_TO_BLT_BUFFER 1 #define EFI_BLT_BUFFER_TO_VIDEO 2 @@ -676,7 +683,8 @@ struct efi_gop efi_uintn_t *size_of_info, struct efi_gop_mode_info **info); efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number); - efi_status_t (EFIAPI *blt)(struct efi_gop *this, void *buffer, + efi_status_t (EFIAPI *blt)(struct efi_gop *this, + struct efi_gop_pixel *buffer, u32 operation, efi_uintn_t sx, efi_uintn_t sy, efi_uintn_t dx, efi_uintn_t dy, efi_uintn_t width, diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 28818eb781..154f306540 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -56,64 +56,150 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number) return EFI_EXIT(EFI_SUCCESS); } -efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer, +static inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid) +{ + struct efi_gop_pixel blt = { + .reserved = 0, + }; + + blt.blue = (vid & 0x1f) << 3; + vid >>= 5; + blt.green = (vid & 0x3f) << 2; + vid >>= 6; + blt.red = (vid & 0x1f) << 3; + return blt; +} + +static inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt) +{ + return (u16)(blt->red >> 3) << 11 | + (u16)(blt->green >> 2) << 5 | + (u16)(blt->blue >> 3); +} + +efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, u32 operation, efi_uintn_t sx, efi_uintn_t sy, efi_uintn_t dx, efi_uintn_t dy, efi_uintn_t width, efi_uintn_t height, efi_uintn_t delta) { struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); - int i, j, line_len16, line_len32; - void *fb; + efi_uintn_t i, j, linelen; + u32 *fb32 = gopobj->fb; + u16 *fb16 = gopobj->fb; + + if (delta) + linelen = delta; + else + linelen = width; EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this, buffer, operation, sx, sy, dx, dy, width, height, delta); - if (operation != EFI_BLT_BUFFER_TO_VIDEO) + /* Check source rectangle */ + switch (operation) { + case EFI_BLT_VIDEO_FILL: + break; + case EFI_BLT_BUFFER_TO_VIDEO: + if (sx + width > linelen) + return EFI_EXIT(EFI_INVALID_PARAMETER); + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + case EFI_BLT_VIDEO_TO_VIDEO: + if (sx + width > gopobj->info.width || + sy + height > gopobj->info.height) + return EFI_EXIT(EFI_INVALID_PARAMETER); + break; + default: return EFI_EXIT(EFI_INVALID_PARAMETER); + } - fb = gopobj->fb; - line_len16 = gopobj->info.width * sizeof(u16); - line_len32 = gopobj->info.width * sizeof(u32); + /* Check destination rectangle */ + switch (operation) { + case EFI_BLT_VIDEO_FILL: + case EFI_BLT_BUFFER_TO_VIDEO: + case EFI_BLT_VIDEO_TO_VIDEO: + if (dx + width > gopobj->info.width || + dy + height > gopobj->info.height) + return EFI_EXIT(EFI_INVALID_PARAMETER); + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + if (dx + width > linelen) + return EFI_EXIT(EFI_INVALID_PARAMETER); + break; + } - /* Copy the contents line by line */ + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + struct efi_gop_pixel pix; + + /* Read source pixel */ + switch (operation) { + case EFI_BLT_VIDEO_FILL: + pix = *buffer; + break; + case EFI_BLT_BUFFER_TO_VIDEO: + pix = buffer[linelen * (i + sy) + j + sx]; + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + case EFI_BLT_VIDEO_TO_VIDEO: + switch (gopobj->bpix) { +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP32: +#else + case LCD_COLOR32: +#endif + pix = *(struct efi_gop_pixel *)&fb32[ + gopobj->info.width * + (i + sy) + j + sx]; + break; +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP16: +#else + case LCD_COLOR16: +#endif + pix = efi_vid16_to_blt_col(fb16[ + gopobj->info.width * + (i + sy) + j + sx]); + break; + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + break; + } - switch (gopobj->bpix) { + /* Write destination pixel */ + switch (operation) { + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + buffer[linelen * (i + dy) + j + dx] = pix; + break; + case EFI_BLT_BUFFER_TO_VIDEO: + case EFI_BLT_VIDEO_FILL: + case EFI_BLT_VIDEO_TO_VIDEO: + switch (gopobj->bpix) { #ifdef CONFIG_DM_VIDEO - case VIDEO_BPP32: + case VIDEO_BPP32: #else - case LCD_COLOR32: + case LCD_COLOR32: #endif - for (i = 0; i < height; i++) { - u32 *dest = fb + ((i + dy) * line_len32) + - (dx * sizeof(u32)); - u32 *src = buffer + ((i + sy) * line_len32) + - (sx * sizeof(u32)); - - /* Same color format, just memcpy */ - memcpy(dest, src, width * sizeof(u32)); - } - break; + fb32[gopobj->info.width * + (i + dy) + j + dx] = *(u32 *)&pix; + break; #ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: + case VIDEO_BPP16: #else - case LCD_COLOR16: + case LCD_COLOR16: #endif - for (i = 0; i < height; i++) { - u16 *dest = fb + ((i + dy) * line_len16) + - (dx * sizeof(u16)); - u32 *src = buffer + ((i + sy) * line_len32) + - (sx * sizeof(u32)); - - /* Convert from rgb888 to rgb565 */ - for (j = 0; j < width; j++) { - u32 rgb888 = src[j]; - dest[j] = ((((rgb888 >> (16 + 3)) & 0x1f) << 11) | - (((rgb888 >> (8 + 2)) & 0x3f) << 5) | - (((rgb888 >> (0 + 3)) & 0x1f) << 0)); + fb16[gopobj->info.width * + (i + dy) + j + dx] = + efi_blt_col_to_vid16(&pix); + break; + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + break; } } - break; } #ifdef CONFIG_DM_VIDEO -- 2.25.1