blob: ac92109f16d157b2a6cf6b67e937bdb0ca11964b [file] [log] [blame]
Alexander Grafd65783d2016-03-15 18:38:21 +01001/*
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>
Alexander Graf792284f2016-06-05 22:34:31 +020010#include <dm.h>
Alexander Grafd65783d2016-03-15 18:38:21 +010011#include <efi_loader.h>
12#include <inttypes.h>
13#include <lcd.h>
14#include <malloc.h>
Alexander Graf792284f2016-06-05 22:34:31 +020015#include <video.h>
Alexander Grafd65783d2016-03-15 18:38:21 +010016
17DECLARE_GLOBAL_DATA_PTR;
18
19static const efi_guid_t efi_gop_guid = EFI_GOP_GUID;
20
21struct 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;
Alexander Graf792284f2016-06-05 22:34:31 +020029 /* Fields we only have acces to during init */
30 u32 bpix;
Rob Clark92cec962017-07-21 15:00:27 -040031 void *fb;
Alexander Grafd65783d2016-03-15 18:38:21 +010032};
33
34static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
Heinrich Schuchardt8b28d5c2017-10-26 19:25:51 +020035 efi_uintn_t *size_of_info,
Alexander Grafd65783d2016-03-15 18:38:21 +010036 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
49static 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
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +010059static 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
73static 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
Heinrich Schuchardt5f86d332018-03-14 19:57:02 +010080/*
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 */
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +010098efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
Heinrich Schuchardt8b28d5c2017-10-26 19:25:51 +020099 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)
Alexander Grafd65783d2016-03-15 18:38:21 +0100103{
Alexander Graf792284f2016-06-05 22:34:31 +0200104 struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100105 efi_uintn_t i, j, linelen;
106 u32 *fb32 = gopobj->fb;
107 u16 *fb16 = gopobj->fb;
108
Heinrich Schuchardt8b28d5c2017-10-26 19:25:51 +0200109 EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
Alexander Grafd65783d2016-03-15 18:38:21 +0100110 buffer, operation, sx, sy, dx, dy, width, height, delta);
111
Heinrich Schuchardt5f86d332018-03-14 19:57:02 +0100112 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
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100121 /* 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:
Alexander Grafd65783d2016-03-15 18:38:21 +0100136 return EFI_EXIT(EFI_INVALID_PARAMETER);
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100137 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100138
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100139 /* 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 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100153
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100154 for (i = 0; i < height; i++) {
155 for (j = 0; j < width; j++) {
156 struct efi_gop_pixel pix;
Alexander Grafd65783d2016-03-15 18:38:21 +0100157
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100158 /* 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) {
Alexander Graf792284f2016-06-05 22:34:31 +0200169#ifdef CONFIG_DM_VIDEO
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100170 case VIDEO_BPP32:
Alexander Graf792284f2016-06-05 22:34:31 +0200171#else
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100172 case LCD_COLOR32:
Alexander Graf792284f2016-06-05 22:34:31 +0200173#endif
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100174 pix = *(struct efi_gop_pixel *)&fb32[
175 gopobj->info.width *
176 (i + sy) + j + sx];
177 break;
Alexander Graf792284f2016-06-05 22:34:31 +0200178#ifdef CONFIG_DM_VIDEO
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100179 case VIDEO_BPP16:
Alexander Graf792284f2016-06-05 22:34:31 +0200180#else
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100181 case LCD_COLOR16:
Alexander Graf792284f2016-06-05 22:34:31 +0200182#endif
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100183 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 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100192
Heinrich Schuchardta2c715f2018-02-07 22:14:22 +0100193 /* 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;
Alexander Grafd65783d2016-03-15 18:38:21 +0100223 }
224 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100225 }
226
Alexander Graf792284f2016-06-05 22:34:31 +0200227#ifdef CONFIG_DM_VIDEO
228 video_sync_all();
229#else
Alexander Grafd65783d2016-03-15 18:38:21 +0100230 lcd_sync();
Alexander Graf792284f2016-06-05 22:34:31 +0200231#endif
Alexander Grafd65783d2016-03-15 18:38:21 +0100232
233 return EFI_EXIT(EFI_SUCCESS);
234}
235
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100236/*
237 * Install graphical output protocol.
238 *
239 * If no supported video device exists this is not considered as an
240 * error.
241 */
242efi_status_t efi_gop_register(void)
Alexander Grafd65783d2016-03-15 18:38:21 +0100243{
244 struct efi_gop_obj *gopobj;
Alexander Graf792284f2016-06-05 22:34:31 +0200245 u32 bpix, col, row;
Alexander Grafbc766392016-06-07 00:57:05 +0200246 u64 fb_base, fb_size;
Rob Clark92cec962017-07-21 15:00:27 -0400247 void *fb;
Heinrich Schuchardtbde9a9b2017-11-26 14:05:14 +0100248 efi_status_t ret;
Alexander Grafd65783d2016-03-15 18:38:21 +0100249
Alexander Graf792284f2016-06-05 22:34:31 +0200250#ifdef CONFIG_DM_VIDEO
251 struct udevice *vdev;
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100252 struct video_priv *priv;
Alexander Graf792284f2016-06-05 22:34:31 +0200253
254 /* We only support a single video output device for now */
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100255 if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
256 debug("WARNING: No video device\n");
257 return EFI_SUCCESS;
258 }
Alexander Graf792284f2016-06-05 22:34:31 +0200259
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100260 priv = dev_get_uclass_priv(vdev);
Alexander Graf792284f2016-06-05 22:34:31 +0200261 bpix = priv->bpix;
262 col = video_get_xsize(vdev);
263 row = video_get_ysize(vdev);
Alexander Grafbc766392016-06-07 00:57:05 +0200264 fb_base = (uintptr_t)priv->fb;
265 fb_size = priv->fb_size;
Rob Clark92cec962017-07-21 15:00:27 -0400266 fb = priv->fb;
Alexander Graf792284f2016-06-05 22:34:31 +0200267#else
Alexander Grafbc766392016-06-07 00:57:05 +0200268 int line_len;
Alexander Graf792284f2016-06-05 22:34:31 +0200269
270 bpix = panel_info.vl_bpix;
271 col = panel_info.vl_col;
272 row = panel_info.vl_row;
Alexander Grafbc766392016-06-07 00:57:05 +0200273 fb_base = gd->fb_base;
274 fb_size = lcd_get_size(&line_len);
Alexander Grafda29c602017-07-31 09:15:57 +0200275 fb = (void*)gd->fb_base;
Alexander Graf792284f2016-06-05 22:34:31 +0200276#endif
277
278 switch (bpix) {
279#ifdef CONFIG_DM_VIDEO
280 case VIDEO_BPP16:
281 case VIDEO_BPP32:
282#else
Alexander Grafd65783d2016-03-15 18:38:21 +0100283 case LCD_COLOR32:
284 case LCD_COLOR16:
Alexander Graf792284f2016-06-05 22:34:31 +0200285#endif
Alexander Grafd65783d2016-03-15 18:38:21 +0100286 break;
287 default:
288 /* So far, we only work in 16 or 32 bit mode */
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100289 debug("WARNING: Unsupported video mode\n");
290 return EFI_SUCCESS;
Alexander Grafd65783d2016-03-15 18:38:21 +0100291 }
292
293 gopobj = calloc(1, sizeof(*gopobj));
Heinrich Schuchardt9aaa35b2017-10-26 19:25:45 +0200294 if (!gopobj) {
295 printf("ERROR: Out of memory\n");
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100296 return EFI_OUT_OF_RESOURCES;
Heinrich Schuchardt9aaa35b2017-10-26 19:25:45 +0200297 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100298
Heinrich Schuchardtbde9a9b2017-11-26 14:05:14 +0100299 /* Hook up to the device list */
Heinrich Schuchardt967d7de2017-11-26 14:05:23 +0100300 efi_add_handle(&gopobj->parent);
Heinrich Schuchardtbde9a9b2017-11-26 14:05:14 +0100301
Alexander Grafd65783d2016-03-15 18:38:21 +0100302 /* Fill in object data */
Heinrich Schuchardtbde9a9b2017-11-26 14:05:14 +0100303 ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
304 &gopobj->ops);
305 if (ret != EFI_SUCCESS) {
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100306 printf("ERROR: Failure adding gop protocol\n");
307 return ret;
Heinrich Schuchardtbde9a9b2017-11-26 14:05:14 +0100308 }
Alexander Grafd65783d2016-03-15 18:38:21 +0100309 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);
Alexander Grafd65783d2016-03-15 18:38:21 +0100317
Alexander Grafbc766392016-06-07 00:57:05 +0200318#ifdef CONFIG_DM_VIDEO
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100319 if (bpix == VIDEO_BPP32)
Alexander Grafbc766392016-06-07 00:57:05 +0200320#else
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100321 if (bpix == LCD_COLOR32)
Alexander Grafbc766392016-06-07 00:57:05 +0200322#endif
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100323 {
Alexander Grafbc766392016-06-07 00:57:05 +0200324 /* 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
Alexander Grafd65783d2016-03-15 18:38:21 +0100329 gopobj->info.version = 0;
Alexander Graf792284f2016-06-05 22:34:31 +0200330 gopobj->info.width = col;
331 gopobj->info.height = row;
Alexander Grafd65783d2016-03-15 18:38:21 +0100332 gopobj->info.pixel_format = EFI_GOT_RGBA8;
Alexander Graf792284f2016-06-05 22:34:31 +0200333 gopobj->info.pixels_per_scanline = col;
334
335 gopobj->bpix = bpix;
Rob Clark92cec962017-07-21 15:00:27 -0400336 gopobj->fb = fb;
Alexander Grafd65783d2016-03-15 18:38:21 +0100337
Heinrich Schuchardtb91c0842018-03-03 15:28:55 +0100338 return EFI_SUCCESS;
Alexander Grafd65783d2016-03-15 18:38:21 +0100339}