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