2 * EFI application disk support
4 * Copyright (c) 2016 Alexander Graf
6 * SPDX-License-Identifier: GPL-2.0+
11 #include <efi_loader.h>
17 DECLARE_GLOBAL_DATA_PTR;
19 static const efi_guid_t efi_gop_guid = EFI_GOP_GUID;
22 /* Generic EFI object parent class data */
23 struct efi_object parent;
24 /* EFI Interface callback struct for gop */
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 */
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)
38 struct efi_gop_obj *gopobj;
40 EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
42 gopobj = container_of(this, struct efi_gop_obj, ops);
43 *size_of_info = sizeof(gopobj->info);
44 *info = &gopobj->info;
46 return EFI_EXIT(EFI_SUCCESS);
49 static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
51 EFI_ENTRY("%p, %x", this, mode_number);
54 return EFI_EXIT(EFI_INVALID_PARAMETER);
56 return EFI_EXIT(EFI_SUCCESS);
59 static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
61 struct efi_gop_pixel blt = {
65 blt.blue = (vid & 0x1f) << 3;
67 blt.green = (vid & 0x3f) << 2;
69 blt.red = (vid & 0x1f) << 3;
73 static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
75 return (u16)(blt->red >> 3) << 11 |
76 (u16)(blt->green >> 2) << 5 |
77 (u16)(blt->blue >> 3);
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,
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);
97 /* Check for 4 byte alignment */
99 return EFI_INVALID_PARAMETER;
100 linelen = delta >> 2;
105 /* Check source rectangle */
107 case EFI_BLT_VIDEO_FILL:
109 case EFI_BLT_BUFFER_TO_VIDEO:
110 if (sx + width > linelen)
111 return EFI_INVALID_PARAMETER;
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;
120 return EFI_INVALID_PARAMETER;
123 /* Check destination rectangle */
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;
132 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
133 if (dx + width > linelen)
134 return EFI_INVALID_PARAMETER;
138 /* Calculate line width */
140 case EFI_BLT_BUFFER_TO_VIDEO:
143 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
144 case EFI_BLT_VIDEO_TO_VIDEO:
145 swidth = gopobj->info.width;
147 return EFI_UNSUPPORTED;
149 case EFI_BLT_VIDEO_FILL:
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;
160 return EFI_UNSUPPORTED;
162 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
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;
173 /* Read source pixel */
175 case EFI_BLT_VIDEO_FILL:
178 case EFI_BLT_BUFFER_TO_VIDEO:
179 pix = buffer[slineoff + j + sx];
181 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
182 case EFI_BLT_VIDEO_TO_VIDEO:
184 pix = *(struct efi_gop_pixel *)&fb32[
187 pix = efi_vid16_to_blt_col(fb16[
192 /* Write destination pixel */
194 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
195 buffer[dlineoff + j + dx] = pix;
197 case EFI_BLT_BUFFER_TO_VIDEO:
198 case EFI_BLT_VIDEO_FILL:
199 case EFI_BLT_VIDEO_TO_VIDEO:
201 fb32[dlineoff + j + dx] = *(u32 *)&pix;
203 fb16[dlineoff + j + dx] =
204 efi_blt_col_to_vid16(&pix);
215 static efi_uintn_t gop_get_bpp(struct efi_gop *this)
217 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
218 efi_uintn_t vid_bpp = 0;
220 switch (gopobj->bpix) {
221 #ifdef CONFIG_DM_VIDEO
228 #ifdef CONFIG_DM_VIDEO
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
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.
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,
257 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
258 dy, width, height, delta, vid_bpp);
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)
268 return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
269 dy, width, height, delta, 16);
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)
279 return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
280 dy, width, height, delta, 32);
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,
291 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
292 dy, width, height, delta, vid_bpp);
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,
303 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
304 dx, dy, width, height, delta, vid_bpp);
310 * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
311 * See the Unified Extensible Firmware Interface (UEFI) specification for
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
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)
331 efi_status_t ret = EFI_INVALID_PARAMETER;
334 EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
335 buffer, operation, sx, sy, dx, dy, width, height, delta);
337 vid_bpp = gop_get_bpp(this);
339 /* Allow for compiler optimization */
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);
345 case EFI_BLT_BUFFER_TO_VIDEO:
346 /* This needs to be super-fast, so duplicate for 16/32bpp */
348 ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
349 sy, dx, dy, width, height,
352 ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
353 sy, dx, dy, width, height,
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);
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);
365 ret = EFI_UNSUPPORTED;
368 if (ret != EFI_SUCCESS)
369 return EFI_EXIT(ret);
371 #ifdef CONFIG_DM_VIDEO
377 return EFI_EXIT(EFI_SUCCESS);
381 * Install graphical output protocol.
383 * If no supported video device exists this is not considered as an
386 efi_status_t efi_gop_register(void)
388 struct efi_gop_obj *gopobj;
390 u64 fb_base, fb_size;
394 #ifdef CONFIG_DM_VIDEO
395 struct udevice *vdev;
396 struct video_priv *priv;
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");
404 priv = dev_get_uclass_priv(vdev);
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;
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;
423 #ifdef CONFIG_DM_VIDEO
432 /* So far, we only work in 16 or 32 bit mode */
433 debug("WARNING: Unsupported video mode\n");
437 gopobj = calloc(1, sizeof(*gopobj));
439 printf("ERROR: Out of memory\n");
440 return EFI_OUT_OF_RESOURCES;
443 /* Hook up to the device list */
444 efi_add_handle(&gopobj->parent);
446 /* Fill in object data */
447 ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
449 if (ret != EFI_SUCCESS) {
450 printf("ERROR: Failure adding gop protocol\n");
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;
458 gopobj->mode.max_mode = 1;
459 gopobj->mode.info = &gopobj->info;
460 gopobj->mode.info_size = sizeof(gopobj->info);
462 #ifdef CONFIG_DM_VIDEO
463 if (bpix == VIDEO_BPP32)
465 if (bpix == LCD_COLOR32)
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;
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;