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