1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * EFI application disk support 4 * 5 * Copyright (c) 2016 Alexander Graf 6 */ 7 8 #include <common.h> 9 #include <dm.h> 10 #include <efi_loader.h> 11 #include <inttypes.h> 12 #include <lcd.h> 13 #include <malloc.h> 14 #include <video.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 static const efi_guid_t efi_gop_guid = EFI_GOP_GUID; 19 20 struct efi_gop_obj { 21 /* Generic EFI object parent class data */ 22 struct efi_object parent; 23 /* EFI Interface callback struct for gop */ 24 struct efi_gop ops; 25 /* The only mode we support */ 26 struct efi_gop_mode_info info; 27 struct efi_gop_mode mode; 28 /* Fields we only have acces to during init */ 29 u32 bpix; 30 void *fb; 31 }; 32 33 static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number, 34 efi_uintn_t *size_of_info, 35 struct efi_gop_mode_info **info) 36 { 37 struct efi_gop_obj *gopobj; 38 39 EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info); 40 41 gopobj = container_of(this, struct efi_gop_obj, ops); 42 *size_of_info = sizeof(gopobj->info); 43 *info = &gopobj->info; 44 45 return EFI_EXIT(EFI_SUCCESS); 46 } 47 48 static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number) 49 { 50 EFI_ENTRY("%p, %x", this, mode_number); 51 52 if (mode_number != 0) 53 return EFI_EXIT(EFI_INVALID_PARAMETER); 54 55 return EFI_EXIT(EFI_SUCCESS); 56 } 57 58 static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid) 59 { 60 struct efi_gop_pixel blt = { 61 .reserved = 0, 62 }; 63 64 blt.blue = (vid & 0x1f) << 3; 65 vid >>= 5; 66 blt.green = (vid & 0x3f) << 2; 67 vid >>= 6; 68 blt.red = (vid & 0x1f) << 3; 69 return blt; 70 } 71 72 static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt) 73 { 74 return (u16)(blt->red >> 3) << 11 | 75 (u16)(blt->green >> 2) << 5 | 76 (u16)(blt->blue >> 3); 77 } 78 79 static __always_inline efi_status_t gop_blt_int(struct efi_gop *this, 80 struct efi_gop_pixel *bufferp, 81 u32 operation, efi_uintn_t sx, 82 efi_uintn_t sy, efi_uintn_t dx, 83 efi_uintn_t dy, 84 efi_uintn_t width, 85 efi_uintn_t height, 86 efi_uintn_t delta, 87 efi_uintn_t vid_bpp) 88 { 89 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); 90 efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth; 91 u32 *fb32 = gopobj->fb; 92 u16 *fb16 = gopobj->fb; 93 struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4); 94 95 if (delta) { 96 /* Check for 4 byte alignment */ 97 if (delta & 3) 98 return EFI_INVALID_PARAMETER; 99 linelen = delta >> 2; 100 } else { 101 linelen = width; 102 } 103 104 /* Check source rectangle */ 105 switch (operation) { 106 case EFI_BLT_VIDEO_FILL: 107 break; 108 case EFI_BLT_BUFFER_TO_VIDEO: 109 if (sx + width > linelen) 110 return EFI_INVALID_PARAMETER; 111 break; 112 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 113 case EFI_BLT_VIDEO_TO_VIDEO: 114 if (sx + width > gopobj->info.width || 115 sy + height > gopobj->info.height) 116 return EFI_INVALID_PARAMETER; 117 break; 118 default: 119 return EFI_INVALID_PARAMETER; 120 } 121 122 /* Check destination rectangle */ 123 switch (operation) { 124 case EFI_BLT_VIDEO_FILL: 125 case EFI_BLT_BUFFER_TO_VIDEO: 126 case EFI_BLT_VIDEO_TO_VIDEO: 127 if (dx + width > gopobj->info.width || 128 dy + height > gopobj->info.height) 129 return EFI_INVALID_PARAMETER; 130 break; 131 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 132 if (dx + width > linelen) 133 return EFI_INVALID_PARAMETER; 134 break; 135 } 136 137 /* Calculate line width */ 138 switch (operation) { 139 case EFI_BLT_BUFFER_TO_VIDEO: 140 swidth = linelen; 141 break; 142 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 143 case EFI_BLT_VIDEO_TO_VIDEO: 144 swidth = gopobj->info.width; 145 if (!vid_bpp) 146 return EFI_UNSUPPORTED; 147 break; 148 case EFI_BLT_VIDEO_FILL: 149 swidth = 0; 150 break; 151 } 152 153 switch (operation) { 154 case EFI_BLT_BUFFER_TO_VIDEO: 155 case EFI_BLT_VIDEO_FILL: 156 case EFI_BLT_VIDEO_TO_VIDEO: 157 dwidth = gopobj->info.width; 158 if (!vid_bpp) 159 return EFI_UNSUPPORTED; 160 break; 161 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 162 dwidth = linelen; 163 break; 164 } 165 166 slineoff = swidth * sy; 167 dlineoff = dwidth * dy; 168 for (i = 0; i < height; i++) { 169 for (j = 0; j < width; j++) { 170 struct efi_gop_pixel pix; 171 172 /* Read source pixel */ 173 switch (operation) { 174 case EFI_BLT_VIDEO_FILL: 175 pix = *buffer; 176 break; 177 case EFI_BLT_BUFFER_TO_VIDEO: 178 pix = buffer[slineoff + j + sx]; 179 break; 180 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 181 case EFI_BLT_VIDEO_TO_VIDEO: 182 if (vid_bpp == 32) 183 pix = *(struct efi_gop_pixel *)&fb32[ 184 slineoff + j + sx]; 185 else 186 pix = efi_vid16_to_blt_col(fb16[ 187 slineoff + j + sx]); 188 break; 189 } 190 191 /* Write destination pixel */ 192 switch (operation) { 193 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 194 buffer[dlineoff + j + dx] = pix; 195 break; 196 case EFI_BLT_BUFFER_TO_VIDEO: 197 case EFI_BLT_VIDEO_FILL: 198 case EFI_BLT_VIDEO_TO_VIDEO: 199 if (vid_bpp == 32) 200 fb32[dlineoff + j + dx] = *(u32 *)&pix; 201 else 202 fb16[dlineoff + j + dx] = 203 efi_blt_col_to_vid16(&pix); 204 break; 205 } 206 } 207 slineoff += swidth; 208 dlineoff += dwidth; 209 } 210 211 return EFI_SUCCESS; 212 } 213 214 static efi_uintn_t gop_get_bpp(struct efi_gop *this) 215 { 216 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); 217 efi_uintn_t vid_bpp = 0; 218 219 switch (gopobj->bpix) { 220 #ifdef CONFIG_DM_VIDEO 221 case VIDEO_BPP32: 222 #else 223 case LCD_COLOR32: 224 #endif 225 vid_bpp = 32; 226 break; 227 #ifdef CONFIG_DM_VIDEO 228 case VIDEO_BPP16: 229 #else 230 case LCD_COLOR16: 231 #endif 232 vid_bpp = 16; 233 break; 234 } 235 236 return vid_bpp; 237 } 238 239 /* 240 * Gcc can't optimize our BLT function well, but we need to make sure that 241 * our 2-dimensional loop gets executed very quickly, otherwise the system 242 * will feel slow. 243 * 244 * By manually putting all obvious branch targets into functions which call 245 * our generic blt function with constants, the compiler can successfully 246 * optimize for speed. 247 */ 248 static efi_status_t gop_blt_video_fill(struct efi_gop *this, 249 struct efi_gop_pixel *buffer, 250 u32 foo, efi_uintn_t sx, 251 efi_uintn_t sy, efi_uintn_t dx, 252 efi_uintn_t dy, efi_uintn_t width, 253 efi_uintn_t height, efi_uintn_t delta, 254 efi_uintn_t vid_bpp) 255 { 256 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx, 257 dy, width, height, delta, vid_bpp); 258 } 259 260 static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this, 261 struct efi_gop_pixel *buffer, 262 u32 foo, efi_uintn_t sx, 263 efi_uintn_t sy, efi_uintn_t dx, 264 efi_uintn_t dy, efi_uintn_t width, 265 efi_uintn_t height, efi_uintn_t delta) 266 { 267 return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx, 268 dy, width, height, delta, 16); 269 } 270 271 static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this, 272 struct efi_gop_pixel *buffer, 273 u32 foo, efi_uintn_t sx, 274 efi_uintn_t sy, efi_uintn_t dx, 275 efi_uintn_t dy, efi_uintn_t width, 276 efi_uintn_t height, efi_uintn_t delta) 277 { 278 return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx, 279 dy, width, height, delta, 32); 280 } 281 282 static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this, 283 struct efi_gop_pixel *buffer, 284 u32 foo, efi_uintn_t sx, 285 efi_uintn_t sy, efi_uintn_t dx, 286 efi_uintn_t dy, efi_uintn_t width, 287 efi_uintn_t height, efi_uintn_t delta, 288 efi_uintn_t vid_bpp) 289 { 290 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx, 291 dy, width, height, delta, vid_bpp); 292 } 293 294 static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this, 295 struct efi_gop_pixel *buffer, 296 u32 foo, efi_uintn_t sx, 297 efi_uintn_t sy, efi_uintn_t dx, 298 efi_uintn_t dy, efi_uintn_t width, 299 efi_uintn_t height, efi_uintn_t delta, 300 efi_uintn_t vid_bpp) 301 { 302 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy, 303 dx, dy, width, height, delta, vid_bpp); 304 } 305 306 /* 307 * Copy rectangle. 308 * 309 * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL. 310 * See the Unified Extensible Firmware Interface (UEFI) specification for 311 * details. 312 * 313 * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL 314 * @buffer: pixel buffer 315 * @sx: source x-coordinate 316 * @sy: source y-coordinate 317 * @dx: destination x-coordinate 318 * @dy: destination y-coordinate 319 * @width: width of rectangle 320 * @height: height of rectangle 321 * @delta: length in bytes of a line in the pixel buffer (optional) 322 * @return: status code 323 */ 324 efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, 325 u32 operation, efi_uintn_t sx, 326 efi_uintn_t sy, efi_uintn_t dx, 327 efi_uintn_t dy, efi_uintn_t width, 328 efi_uintn_t height, efi_uintn_t delta) 329 { 330 efi_status_t ret = EFI_INVALID_PARAMETER; 331 efi_uintn_t vid_bpp; 332 333 EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this, 334 buffer, operation, sx, sy, dx, dy, width, height, delta); 335 336 vid_bpp = gop_get_bpp(this); 337 338 /* Allow for compiler optimization */ 339 switch (operation) { 340 case EFI_BLT_VIDEO_FILL: 341 ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx, 342 dy, width, height, delta, vid_bpp); 343 break; 344 case EFI_BLT_BUFFER_TO_VIDEO: 345 /* This needs to be super-fast, so duplicate for 16/32bpp */ 346 if (vid_bpp == 32) 347 ret = gop_blt_buf_to_vid32(this, buffer, operation, sx, 348 sy, dx, dy, width, height, 349 delta); 350 else 351 ret = gop_blt_buf_to_vid16(this, buffer, operation, sx, 352 sy, dx, dy, width, height, 353 delta); 354 break; 355 case EFI_BLT_VIDEO_TO_VIDEO: 356 ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx, 357 dy, width, height, delta, vid_bpp); 358 break; 359 case EFI_BLT_VIDEO_TO_BLT_BUFFER: 360 ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx, 361 dy, width, height, delta, vid_bpp); 362 break; 363 default: 364 ret = EFI_UNSUPPORTED; 365 } 366 367 if (ret != EFI_SUCCESS) 368 return EFI_EXIT(ret); 369 370 #ifdef CONFIG_DM_VIDEO 371 video_sync_all(); 372 #else 373 lcd_sync(); 374 #endif 375 376 return EFI_EXIT(EFI_SUCCESS); 377 } 378 379 /* 380 * Install graphical output protocol. 381 * 382 * If no supported video device exists this is not considered as an 383 * error. 384 */ 385 efi_status_t efi_gop_register(void) 386 { 387 struct efi_gop_obj *gopobj; 388 u32 bpix, col, row; 389 u64 fb_base, fb_size; 390 void *fb; 391 efi_status_t ret; 392 393 #ifdef CONFIG_DM_VIDEO 394 struct udevice *vdev; 395 struct video_priv *priv; 396 397 /* We only support a single video output device for now */ 398 if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) { 399 debug("WARNING: No video device\n"); 400 return EFI_SUCCESS; 401 } 402 403 priv = dev_get_uclass_priv(vdev); 404 bpix = priv->bpix; 405 col = video_get_xsize(vdev); 406 row = video_get_ysize(vdev); 407 fb_base = (uintptr_t)priv->fb; 408 fb_size = priv->fb_size; 409 fb = priv->fb; 410 #else 411 int line_len; 412 413 bpix = panel_info.vl_bpix; 414 col = panel_info.vl_col; 415 row = panel_info.vl_row; 416 fb_base = gd->fb_base; 417 fb_size = lcd_get_size(&line_len); 418 fb = (void*)gd->fb_base; 419 #endif 420 421 switch (bpix) { 422 #ifdef CONFIG_DM_VIDEO 423 case VIDEO_BPP16: 424 case VIDEO_BPP32: 425 #else 426 case LCD_COLOR32: 427 case LCD_COLOR16: 428 #endif 429 break; 430 default: 431 /* So far, we only work in 16 or 32 bit mode */ 432 debug("WARNING: Unsupported video mode\n"); 433 return EFI_SUCCESS; 434 } 435 436 gopobj = calloc(1, sizeof(*gopobj)); 437 if (!gopobj) { 438 printf("ERROR: Out of memory\n"); 439 return EFI_OUT_OF_RESOURCES; 440 } 441 442 /* Hook up to the device list */ 443 efi_add_handle(&gopobj->parent); 444 445 /* Fill in object data */ 446 ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid, 447 &gopobj->ops); 448 if (ret != EFI_SUCCESS) { 449 printf("ERROR: Failure adding gop protocol\n"); 450 return ret; 451 } 452 gopobj->ops.query_mode = gop_query_mode; 453 gopobj->ops.set_mode = gop_set_mode; 454 gopobj->ops.blt = gop_blt; 455 gopobj->ops.mode = &gopobj->mode; 456 457 gopobj->mode.max_mode = 1; 458 gopobj->mode.info = &gopobj->info; 459 gopobj->mode.info_size = sizeof(gopobj->info); 460 461 #ifdef CONFIG_DM_VIDEO 462 if (bpix == VIDEO_BPP32) 463 #else 464 if (bpix == LCD_COLOR32) 465 #endif 466 { 467 /* With 32bit color space we can directly expose the fb */ 468 gopobj->mode.fb_base = fb_base; 469 gopobj->mode.fb_size = fb_size; 470 } 471 472 gopobj->info.version = 0; 473 gopobj->info.width = col; 474 gopobj->info.height = row; 475 gopobj->info.pixel_format = EFI_GOT_RGBA8; 476 gopobj->info.pixels_per_scanline = col; 477 478 gopobj->bpix = bpix; 479 gopobj->fb = fb; 480 481 return EFI_SUCCESS; 482 } 483