efi_loader: use __always_inline for pixel conversion
[oweals/u-boot.git] / lib / efi_loader / efi_gop.c
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 /*
81  * Copy rectangle.
82  *
83  * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
84  * See the Unified Extensible Firmware Interface (UEFI) specification for
85  * details.
86  *
87  * @this:       EFI_GRAPHICS_OUTPUT_PROTOCOL
88  * @buffer:     pixel buffer
89  * @sx:         source x-coordinate
90  * @sy:         source y-coordinate
91  * @dx:         destination x-coordinate
92  * @dy:         destination y-coordinate
93  * @width:      width of rectangle
94  * @height:     height of rectangle
95  * @delta:      length in bytes of a line in the pixel buffer (optional)
96  * @return:     status code
97  */
98 efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
99                             u32 operation, efi_uintn_t sx,
100                             efi_uintn_t sy, efi_uintn_t dx,
101                             efi_uintn_t dy, efi_uintn_t width,
102                             efi_uintn_t height, efi_uintn_t delta)
103 {
104         struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
105         efi_uintn_t i, j, linelen;
106         u32 *fb32 = gopobj->fb;
107         u16 *fb16 = gopobj->fb;
108
109         EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
110                   buffer, operation, sx, sy, dx, dy, width, height, delta);
111
112         if (delta) {
113                 /* Check for 4 byte alignment */
114                 if (delta & 3)
115                         return EFI_EXIT(EFI_INVALID_PARAMETER);
116                 linelen = delta >> 2;
117         } else {
118                 linelen = width;
119         }
120
121         /* Check source rectangle */
122         switch (operation) {
123         case EFI_BLT_VIDEO_FILL:
124                 break;
125         case EFI_BLT_BUFFER_TO_VIDEO:
126                 if (sx + width > linelen)
127                         return EFI_EXIT(EFI_INVALID_PARAMETER);
128                 break;
129         case EFI_BLT_VIDEO_TO_BLT_BUFFER:
130         case EFI_BLT_VIDEO_TO_VIDEO:
131                 if (sx + width > gopobj->info.width ||
132                     sy + height > gopobj->info.height)
133                         return EFI_EXIT(EFI_INVALID_PARAMETER);
134                 break;
135         default:
136                 return EFI_EXIT(EFI_INVALID_PARAMETER);
137         }
138
139         /* Check destination rectangle */
140         switch (operation) {
141         case EFI_BLT_VIDEO_FILL:
142         case EFI_BLT_BUFFER_TO_VIDEO:
143         case EFI_BLT_VIDEO_TO_VIDEO:
144                 if (dx + width > gopobj->info.width ||
145                     dy + height > gopobj->info.height)
146                         return EFI_EXIT(EFI_INVALID_PARAMETER);
147                 break;
148         case EFI_BLT_VIDEO_TO_BLT_BUFFER:
149                 if (dx + width > linelen)
150                         return EFI_EXIT(EFI_INVALID_PARAMETER);
151                 break;
152         }
153
154         for (i = 0; i < height; i++) {
155                 for (j = 0; j < width; j++) {
156                         struct efi_gop_pixel pix;
157
158                         /* Read source pixel */
159                         switch (operation) {
160                         case EFI_BLT_VIDEO_FILL:
161                                 pix = *buffer;
162                                 break;
163                         case EFI_BLT_BUFFER_TO_VIDEO:
164                                 pix = buffer[linelen * (i + sy) + j + sx];
165                                 break;
166                         case EFI_BLT_VIDEO_TO_BLT_BUFFER:
167                         case EFI_BLT_VIDEO_TO_VIDEO:
168                                 switch (gopobj->bpix) {
169 #ifdef CONFIG_DM_VIDEO
170                                 case VIDEO_BPP32:
171 #else
172                                 case LCD_COLOR32:
173 #endif
174                                         pix = *(struct efi_gop_pixel *)&fb32[
175                                                 gopobj->info.width *
176                                                 (i + sy) + j + sx];
177                                 break;
178 #ifdef CONFIG_DM_VIDEO
179                                 case VIDEO_BPP16:
180 #else
181                                 case LCD_COLOR16:
182 #endif
183                                         pix = efi_vid16_to_blt_col(fb16[
184                                                 gopobj->info.width *
185                                                 (i + sy) + j + sx]);
186                                         break;
187                                 default:
188                                         return EFI_EXIT(EFI_UNSUPPORTED);
189                                 }
190                                 break;
191                         }
192
193                         /* Write destination pixel */
194                         switch (operation) {
195                         case EFI_BLT_VIDEO_TO_BLT_BUFFER:
196                                 buffer[linelen * (i + dy) + j + dx] = pix;
197                                 break;
198                         case EFI_BLT_BUFFER_TO_VIDEO:
199                         case EFI_BLT_VIDEO_FILL:
200                         case EFI_BLT_VIDEO_TO_VIDEO:
201                                 switch (gopobj->bpix) {
202 #ifdef CONFIG_DM_VIDEO
203                                 case VIDEO_BPP32:
204 #else
205                                 case LCD_COLOR32:
206 #endif
207                                         fb32[gopobj->info.width *
208                                              (i + dy) + j + dx] = *(u32 *)&pix;
209                                         break;
210 #ifdef CONFIG_DM_VIDEO
211                                 case VIDEO_BPP16:
212 #else
213                                 case LCD_COLOR16:
214 #endif
215                                         fb16[gopobj->info.width *
216                                              (i + dy) + j + dx] =
217                                                 efi_blt_col_to_vid16(&pix);
218                                         break;
219                                 default:
220                                         return EFI_EXIT(EFI_UNSUPPORTED);
221                                 }
222                                 break;
223                         }
224                 }
225         }
226
227 #ifdef CONFIG_DM_VIDEO
228         video_sync_all();
229 #else
230         lcd_sync();
231 #endif
232
233         return EFI_EXIT(EFI_SUCCESS);
234 }
235
236 /*
237  * Install graphical output protocol.
238  *
239  * If no supported video device exists this is not considered as an
240  * error.
241  */
242 efi_status_t efi_gop_register(void)
243 {
244         struct efi_gop_obj *gopobj;
245         u32 bpix, col, row;
246         u64 fb_base, fb_size;
247         void *fb;
248         efi_status_t ret;
249
250 #ifdef CONFIG_DM_VIDEO
251         struct udevice *vdev;
252         struct video_priv *priv;
253
254         /* We only support a single video output device for now */
255         if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
256                 debug("WARNING: No video device\n");
257                 return EFI_SUCCESS;
258         }
259
260         priv = dev_get_uclass_priv(vdev);
261         bpix = priv->bpix;
262         col = video_get_xsize(vdev);
263         row = video_get_ysize(vdev);
264         fb_base = (uintptr_t)priv->fb;
265         fb_size = priv->fb_size;
266         fb = priv->fb;
267 #else
268         int line_len;
269
270         bpix = panel_info.vl_bpix;
271         col = panel_info.vl_col;
272         row = panel_info.vl_row;
273         fb_base = gd->fb_base;
274         fb_size = lcd_get_size(&line_len);
275         fb = (void*)gd->fb_base;
276 #endif
277
278         switch (bpix) {
279 #ifdef CONFIG_DM_VIDEO
280         case VIDEO_BPP16:
281         case VIDEO_BPP32:
282 #else
283         case LCD_COLOR32:
284         case LCD_COLOR16:
285 #endif
286                 break;
287         default:
288                 /* So far, we only work in 16 or 32 bit mode */
289                 debug("WARNING: Unsupported video mode\n");
290                 return EFI_SUCCESS;
291         }
292
293         gopobj = calloc(1, sizeof(*gopobj));
294         if (!gopobj) {
295                 printf("ERROR: Out of memory\n");
296                 return EFI_OUT_OF_RESOURCES;
297         }
298
299         /* Hook up to the device list */
300         efi_add_handle(&gopobj->parent);
301
302         /* Fill in object data */
303         ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
304                                &gopobj->ops);
305         if (ret != EFI_SUCCESS) {
306                 printf("ERROR: Failure adding gop protocol\n");
307                 return ret;
308         }
309         gopobj->ops.query_mode = gop_query_mode;
310         gopobj->ops.set_mode = gop_set_mode;
311         gopobj->ops.blt = gop_blt;
312         gopobj->ops.mode = &gopobj->mode;
313
314         gopobj->mode.max_mode = 1;
315         gopobj->mode.info = &gopobj->info;
316         gopobj->mode.info_size = sizeof(gopobj->info);
317
318 #ifdef CONFIG_DM_VIDEO
319         if (bpix == VIDEO_BPP32)
320 #else
321         if (bpix == LCD_COLOR32)
322 #endif
323         {
324                 /* With 32bit color space we can directly expose the fb */
325                 gopobj->mode.fb_base = fb_base;
326                 gopobj->mode.fb_size = fb_size;
327         }
328
329         gopobj->info.version = 0;
330         gopobj->info.width = col;
331         gopobj->info.height = row;
332         gopobj->info.pixel_format = EFI_GOT_RGBA8;
333         gopobj->info.pixels_per_scanline = col;
334
335         gopobj->bpix = bpix;
336         gopobj->fb = fb;
337
338         return EFI_SUCCESS;
339 }