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 *buffer,
82 u32 operation, efi_uintn_t sx,
83 efi_uintn_t sy, efi_uintn_t dx,
89 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
90 efi_uintn_t i, j, linelen;
91 u32 *fb32 = gopobj->fb;
92 u16 *fb16 = gopobj->fb;
95 /* Check for 4 byte alignment */
97 return EFI_INVALID_PARAMETER;
103 /* Check source rectangle */
105 case EFI_BLT_VIDEO_FILL:
107 case EFI_BLT_BUFFER_TO_VIDEO:
108 if (sx + width > linelen)
109 return EFI_INVALID_PARAMETER;
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;
118 return EFI_INVALID_PARAMETER;
121 /* Check destination rectangle */
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;
130 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
131 if (dx + width > linelen)
132 return EFI_INVALID_PARAMETER;
136 for (i = 0; i < height; i++) {
137 for (j = 0; j < width; j++) {
138 struct efi_gop_pixel pix;
140 /* Read source pixel */
142 case EFI_BLT_VIDEO_FILL:
145 case EFI_BLT_BUFFER_TO_VIDEO:
146 pix = buffer[linelen * (i + sy) + j + sx];
148 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
149 case EFI_BLT_VIDEO_TO_VIDEO:
150 switch (gopobj->bpix) {
151 #ifdef CONFIG_DM_VIDEO
156 pix = *(struct efi_gop_pixel *)&fb32[
160 #ifdef CONFIG_DM_VIDEO
165 pix = efi_vid16_to_blt_col(fb16[
170 return EFI_UNSUPPORTED;
175 /* Write destination pixel */
177 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
178 buffer[linelen * (i + dy) + j + dx] = pix;
180 case EFI_BLT_BUFFER_TO_VIDEO:
181 case EFI_BLT_VIDEO_FILL:
182 case EFI_BLT_VIDEO_TO_VIDEO:
183 switch (gopobj->bpix) {
184 #ifdef CONFIG_DM_VIDEO
189 fb32[gopobj->info.width *
190 (i + dy) + j + dx] = *(u32 *)&pix;
192 #ifdef CONFIG_DM_VIDEO
197 fb16[gopobj->info.width *
199 efi_blt_col_to_vid16(&pix);
202 return EFI_UNSUPPORTED;
213 * Gcc can't optimize our BLT function well, but we need to make sure that
214 * our 2-dimensional loop gets executed very quickly, otherwise the system
217 * By manually putting all obvious branch targets into functions which call
218 * our generic blt function with constants, the compiler can successfully
219 * optimize for speed.
221 static efi_status_t gop_blt_video_fill(struct efi_gop *this,
222 struct efi_gop_pixel *buffer,
223 u32 foo, efi_uintn_t sx,
224 efi_uintn_t sy, efi_uintn_t dx,
225 efi_uintn_t dy, efi_uintn_t width,
226 efi_uintn_t height, efi_uintn_t delta)
228 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
229 dy, width, height, delta);
232 static efi_status_t gop_blt_buf_to_vid(struct efi_gop *this,
233 struct efi_gop_pixel *buffer,
234 u32 foo, efi_uintn_t sx,
235 efi_uintn_t sy, efi_uintn_t dx,
236 efi_uintn_t dy, efi_uintn_t width,
237 efi_uintn_t height, efi_uintn_t delta)
239 return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
240 dy, width, height, delta);
243 static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
244 struct efi_gop_pixel *buffer,
245 u32 foo, efi_uintn_t sx,
246 efi_uintn_t sy, efi_uintn_t dx,
247 efi_uintn_t dy, efi_uintn_t width,
248 efi_uintn_t height, efi_uintn_t delta)
250 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
251 dy, width, height, delta);
254 static efi_status_t gop_blt_vid_to_buf(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)
261 return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
262 dx, dy, width, height, delta);
268 * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
269 * See the Unified Extensible Firmware Interface (UEFI) specification for
272 * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
273 * @buffer: pixel buffer
274 * @sx: source x-coordinate
275 * @sy: source y-coordinate
276 * @dx: destination x-coordinate
277 * @dy: destination y-coordinate
278 * @width: width of rectangle
279 * @height: height of rectangle
280 * @delta: length in bytes of a line in the pixel buffer (optional)
281 * @return: status code
283 efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
284 u32 operation, 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)
289 efi_status_t ret = EFI_INVALID_PARAMETER;
291 EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
292 buffer, operation, sx, sy, dx, dy, width, height, delta);
294 /* Allow for compiler optimization */
296 case EFI_BLT_VIDEO_FILL:
297 ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
298 dy, width, height, delta);
300 case EFI_BLT_BUFFER_TO_VIDEO:
301 ret = gop_blt_buf_to_vid(this, buffer, operation, sx, sy, dx,
302 dy, width, height, delta);
304 case EFI_BLT_VIDEO_TO_VIDEO:
305 ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
306 dy, width, height, delta);
308 case EFI_BLT_VIDEO_TO_BLT_BUFFER:
309 ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
310 dy, width, height, delta);
313 ret = EFI_UNSUPPORTED;
316 if (ret != EFI_SUCCESS)
317 return EFI_EXIT(ret);
319 #ifdef CONFIG_DM_VIDEO
325 return EFI_EXIT(EFI_SUCCESS);
329 * Install graphical output protocol.
331 * If no supported video device exists this is not considered as an
334 efi_status_t efi_gop_register(void)
336 struct efi_gop_obj *gopobj;
338 u64 fb_base, fb_size;
342 #ifdef CONFIG_DM_VIDEO
343 struct udevice *vdev;
344 struct video_priv *priv;
346 /* We only support a single video output device for now */
347 if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
348 debug("WARNING: No video device\n");
352 priv = dev_get_uclass_priv(vdev);
354 col = video_get_xsize(vdev);
355 row = video_get_ysize(vdev);
356 fb_base = (uintptr_t)priv->fb;
357 fb_size = priv->fb_size;
362 bpix = panel_info.vl_bpix;
363 col = panel_info.vl_col;
364 row = panel_info.vl_row;
365 fb_base = gd->fb_base;
366 fb_size = lcd_get_size(&line_len);
367 fb = (void*)gd->fb_base;
371 #ifdef CONFIG_DM_VIDEO
380 /* So far, we only work in 16 or 32 bit mode */
381 debug("WARNING: Unsupported video mode\n");
385 gopobj = calloc(1, sizeof(*gopobj));
387 printf("ERROR: Out of memory\n");
388 return EFI_OUT_OF_RESOURCES;
391 /* Hook up to the device list */
392 efi_add_handle(&gopobj->parent);
394 /* Fill in object data */
395 ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
397 if (ret != EFI_SUCCESS) {
398 printf("ERROR: Failure adding gop protocol\n");
401 gopobj->ops.query_mode = gop_query_mode;
402 gopobj->ops.set_mode = gop_set_mode;
403 gopobj->ops.blt = gop_blt;
404 gopobj->ops.mode = &gopobj->mode;
406 gopobj->mode.max_mode = 1;
407 gopobj->mode.info = &gopobj->info;
408 gopobj->mode.info_size = sizeof(gopobj->info);
410 #ifdef CONFIG_DM_VIDEO
411 if (bpix == VIDEO_BPP32)
413 if (bpix == LCD_COLOR32)
416 /* With 32bit color space we can directly expose the fb */
417 gopobj->mode.fb_base = fb_base;
418 gopobj->mode.fb_size = fb_size;
421 gopobj->info.version = 0;
422 gopobj->info.width = col;
423 gopobj->info.height = row;
424 gopobj->info.pixel_format = EFI_GOT_RGBA8;
425 gopobj->info.pixels_per_scanline = col;