1 /* 2 * EFI application disk support 3 * 4 * Copyright (c) 2016 Alexander Graf 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <dm.h> 11 #include <efi_loader.h> 12 #include <inttypes.h> 13 #include <lcd.h> 14 #include <malloc.h> 15 #include <video.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 static const efi_guid_t efi_gop_guid = EFI_GOP_GUID; 20 21 struct efi_gop_obj { 22 /* Generic EFI object parent class data */ 23 struct efi_object parent; 24 /* EFI Interface callback struct for gop */ 25 struct efi_gop ops; 26 /* The only mode we support */ 27 struct efi_gop_mode_info info; 28 struct efi_gop_mode mode; 29 /* Fields we only have acces to during init */ 30 u32 bpix; 31 void *fb; 32 }; 33 34 static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number, 35 efi_uintn_t *size_of_info, 36 struct efi_gop_mode_info **info) 37 { 38 struct efi_gop_obj *gopobj; 39 40 EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info); 41 42 gopobj = container_of(this, struct efi_gop_obj, ops); 43 *size_of_info = sizeof(gopobj->info); 44 *info = &gopobj->info; 45 46 return EFI_EXIT(EFI_SUCCESS); 47 } 48 49 static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number) 50 { 51 EFI_ENTRY("%p, %x", this, mode_number); 52 53 if (mode_number != 0) 54 return EFI_EXIT(EFI_INVALID_PARAMETER); 55 56 return EFI_EXIT(EFI_SUCCESS); 57 } 58 59 efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer, 60 u32 operation, efi_uintn_t sx, 61 efi_uintn_t sy, efi_uintn_t dx, 62 efi_uintn_t dy, efi_uintn_t width, 63 efi_uintn_t height, efi_uintn_t delta) 64 { 65 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); 66 int i, j, line_len16, line_len32; 67 void *fb; 68 69 EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this, 70 buffer, operation, sx, sy, dx, dy, width, height, delta); 71 72 if (operation != EFI_BLT_BUFFER_TO_VIDEO) 73 return EFI_EXIT(EFI_INVALID_PARAMETER); 74 75 fb = gopobj->fb; 76 line_len16 = gopobj->info.width * sizeof(u16); 77 line_len32 = gopobj->info.width * sizeof(u32); 78 79 /* Copy the contents line by line */ 80 81 switch (gopobj->bpix) { 82 #ifdef CONFIG_DM_VIDEO 83 case VIDEO_BPP32: 84 #else 85 case LCD_COLOR32: 86 #endif 87 for (i = 0; i < height; i++) { 88 u32 *dest = fb + ((i + dy) * line_len32) + 89 (dx * sizeof(u32)); 90 u32 *src = buffer + ((i + sy) * line_len32) + 91 (sx * sizeof(u32)); 92 93 /* Same color format, just memcpy */ 94 memcpy(dest, src, width * sizeof(u32)); 95 } 96 break; 97 #ifdef CONFIG_DM_VIDEO 98 case VIDEO_BPP16: 99 #else 100 case LCD_COLOR16: 101 #endif 102 for (i = 0; i < height; i++) { 103 u16 *dest = fb + ((i + dy) * line_len16) + 104 (dx * sizeof(u16)); 105 u32 *src = buffer + ((i + sy) * line_len32) + 106 (sx * sizeof(u32)); 107 108 /* Convert from rgb888 to rgb565 */ 109 for (j = 0; j < width; j++) { 110 u32 rgb888 = src[j]; 111 dest[j] = ((((rgb888 >> (16 + 3)) & 0x1f) << 11) | 112 (((rgb888 >> (8 + 2)) & 0x3f) << 5) | 113 (((rgb888 >> (0 + 3)) & 0x1f) << 0)); 114 } 115 } 116 break; 117 } 118 119 #ifdef CONFIG_DM_VIDEO 120 video_sync_all(); 121 #else 122 lcd_sync(); 123 #endif 124 125 return EFI_EXIT(EFI_SUCCESS); 126 } 127 128 /* This gets called from do_bootefi_exec(). */ 129 int efi_gop_register(void) 130 { 131 struct efi_gop_obj *gopobj; 132 u32 bpix, col, row; 133 u64 fb_base, fb_size; 134 void *fb; 135 efi_status_t ret; 136 137 #ifdef CONFIG_DM_VIDEO 138 struct udevice *vdev; 139 140 /* We only support a single video output device for now */ 141 if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) 142 return -1; 143 144 struct video_priv *priv = dev_get_uclass_priv(vdev); 145 bpix = priv->bpix; 146 col = video_get_xsize(vdev); 147 row = video_get_ysize(vdev); 148 fb_base = (uintptr_t)priv->fb; 149 fb_size = priv->fb_size; 150 fb = priv->fb; 151 #else 152 int line_len; 153 154 bpix = panel_info.vl_bpix; 155 col = panel_info.vl_col; 156 row = panel_info.vl_row; 157 fb_base = gd->fb_base; 158 fb_size = lcd_get_size(&line_len); 159 fb = (void*)gd->fb_base; 160 #endif 161 162 switch (bpix) { 163 #ifdef CONFIG_DM_VIDEO 164 case VIDEO_BPP16: 165 case VIDEO_BPP32: 166 #else 167 case LCD_COLOR32: 168 case LCD_COLOR16: 169 #endif 170 break; 171 default: 172 /* So far, we only work in 16 or 32 bit mode */ 173 return -1; 174 } 175 176 gopobj = calloc(1, sizeof(*gopobj)); 177 if (!gopobj) { 178 printf("ERROR: Out of memory\n"); 179 return 1; 180 } 181 182 /* Hook up to the device list */ 183 efi_add_handle(&gopobj->parent); 184 185 /* Fill in object data */ 186 ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid, 187 &gopobj->ops); 188 if (ret != EFI_SUCCESS) { 189 printf("ERROR: Out of memory\n"); 190 return 1; 191 } 192 gopobj->ops.query_mode = gop_query_mode; 193 gopobj->ops.set_mode = gop_set_mode; 194 gopobj->ops.blt = gop_blt; 195 gopobj->ops.mode = &gopobj->mode; 196 197 gopobj->mode.max_mode = 1; 198 gopobj->mode.info = &gopobj->info; 199 gopobj->mode.info_size = sizeof(gopobj->info); 200 201 #ifdef CONFIG_DM_VIDEO 202 if (bpix == VIDEO_BPP32) { 203 #else 204 if (bpix == LCD_COLOR32) { 205 #endif 206 /* With 32bit color space we can directly expose the fb */ 207 gopobj->mode.fb_base = fb_base; 208 gopobj->mode.fb_size = fb_size; 209 } 210 211 gopobj->info.version = 0; 212 gopobj->info.width = col; 213 gopobj->info.height = row; 214 gopobj->info.pixel_format = EFI_GOT_RGBA8; 215 gopobj->info.pixels_per_scanline = col; 216 217 gopobj->bpix = bpix; 218 gopobj->fb = fb; 219 220 return 0; 221 } 222