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