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