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