1 /**
2 * @file lv_uefi_display.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "../../lvgl.h"
11
12 #if LV_USE_UEFI
13
14 #include "lv_uefi_display.h"
15 #include "lv_uefi_private.h"
16
17 #if LV_COLOR_DEPTH != 32
18 #error [lv_uefi] Unsupported LV_COLOR_DEPTH.
19 #endif
20
21 /*********************
22 * DEFINES
23 *********************/
24
25 /**********************
26 * TYPEDEFS
27 **********************/
28 typedef struct _lv_uefi_display_context_t {
29 EFI_HANDLE handle;
30 EFI_GRAPHICS_OUTPUT_PROTOCOL * gop_protocol;
31 void * buffer;
32 size_t buffer_size;
33 } lv_uefi_display_context_t;
34
35 /**********************
36 * STATIC PROTOTYPES
37 **********************/
38
39 static void _display_event_cb(lv_event_t * e);
40 static void _display_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map);
41
42 static void _display_ctx_free(lv_uefi_display_context_t * display_ctx);
43 static bool _display_interface_is_valid(const EFI_GRAPHICS_OUTPUT_PROTOCOL * interface);
44
45 /**********************
46 * GOLBAL VARIABLES
47 **********************/
48
49 /**********************
50 * STATIC VARIABLES
51 **********************/
52
53 static EFI_GUID _uefi_guid_graphics_output = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
54 static EFI_GUID _uefi_guid_edid_active = EFI_EDID_ACTIVE_PROTOCOL_GUID;
55
56 /**********************
57 * MACROS
58 **********************/
59
60 /**********************
61 * GLOBAL FUNCTIONS
62 **********************/
63
64 /**
65 * @brief Create a LVGL display object.
66 * @param handle The handle on which an instance of the EFI_GRAPHICS_OUTPUT_PROTOCOL protocol is installed.
67 * @return The created LVGL display object.
68 */
lv_uefi_display_create(void * handle)69 lv_display_t * lv_uefi_display_create(void * handle)
70 {
71 lv_display_t * display = NULL;
72 lv_uefi_display_context_t * display_ctx;
73
74 if(!lv_uefi_protocol_test(handle, &_uefi_guid_graphics_output)) return NULL;
75
76 display_ctx = lv_calloc(1, sizeof(lv_uefi_display_context_t));
77 LV_ASSERT_MALLOC(display_ctx);
78
79 display_ctx->handle = handle;
80 display_ctx->gop_protocol = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_graphics_output);
81 if(!_display_interface_is_valid(display_ctx->gop_protocol)) {
82 LV_LOG_WARN("[lv_uefi] The GOP interface is not valid.");
83 goto error;
84 }
85
86 // 4 bytes per pixel
87 display_ctx->buffer_size = 4 * display_ctx->gop_protocol->Mode->Info->HorizontalResolution *
88 display_ctx->gop_protocol->Mode->Info->VerticalResolution;
89 display_ctx->buffer = lv_malloc(display_ctx->buffer_size);
90 LV_ASSERT_MALLOC(display_ctx->buffer);
91
92 display = lv_display_create(display_ctx->gop_protocol->Mode->Info->HorizontalResolution,
93 display_ctx->gop_protocol->Mode->Info->VerticalResolution);
94 lv_display_add_event_cb(display, _display_event_cb, LV_EVENT_DELETE, display);
95 lv_display_set_flush_cb(display, _display_flush_cb);
96 lv_display_set_buffers(display, display_ctx->buffer, NULL, (uint32_t)display_ctx->buffer_size,
97 LV_DISPLAY_RENDER_MODE_DIRECT);
98 lv_display_set_user_data(display, display_ctx);
99
100 goto finish;
101
102 error:
103 if(display != NULL) {
104 lv_display_set_user_data(display, NULL);
105 lv_display_delete(display);
106 display = NULL;
107 }
108
109 if(display_ctx != NULL) _display_ctx_free(display_ctx);
110
111 finish:
112 return display;
113 }
114
115 /**
116 * @brief Try to find the active display handle.
117 * @return The handle or NULL if not found.
118 * @remark The active display need interfaces for EFI_GRAPHICS_OUTPUT_PROTOCOL and EFI_EDID_ACTIVE_PROTOCOL
119 */
lv_uefi_display_get_active(void)120 void * lv_uefi_display_get_active(void)
121 {
122 EFI_STATUS status;
123 EFI_HANDLE active_handle = NULL;
124 EFI_HANDLE * handles = NULL;
125 UINTN no_handles;
126 UINTN index;
127
128 status = gLvEfiBS->LocateHandleBuffer(
129 ByProtocol,
130 &_uefi_guid_graphics_output,
131 NULL,
132 &no_handles,
133 &handles);
134 if(status != EFI_SUCCESS) goto error;
135
136 for(index = 0; index < no_handles; index++) {
137 if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_edid_active)) continue;
138 if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_graphics_output)) continue;
139 active_handle = handles[index];
140 break;
141 }
142
143 goto finish;
144
145 error:
146
147 finish:
148 if(handles != NULL) gLvEfiBS->FreePool(handles);
149
150 return active_handle;
151 }
152
153 /**
154 * @brief Try to find any display handle.
155 * @return The handle or NULL if not found.
156 */
lv_uefi_display_get_any(void)157 void * lv_uefi_display_get_any(void)
158 {
159 EFI_STATUS status;
160 EFI_HANDLE active_handle = NULL;
161 EFI_HANDLE * handles = NULL;
162 UINTN no_handles;
163 UINTN index;
164
165 status = gLvEfiBS->LocateHandleBuffer(
166 ByProtocol,
167 &_uefi_guid_graphics_output,
168 NULL,
169 &no_handles,
170 &handles);
171 if(status != EFI_SUCCESS) goto error;
172
173 for(index = 0; index < no_handles; index++) {
174 if(!lv_uefi_protocol_test(handles[index], &_uefi_guid_graphics_output)) continue;
175 active_handle = handles[index];
176 break;
177 }
178
179 goto finish;
180
181 error:
182
183 finish:
184 if(handles != NULL) gLvEfiBS->FreePool(handles);
185
186 return active_handle;
187 }
188
189 /**********************
190 * STATIC FUNCTIONS
191 **********************/
192
_display_event_cb(lv_event_t * e)193 static void _display_event_cb(lv_event_t * e)
194 {
195 lv_display_t * display;
196 lv_uefi_display_context_t * display_ctx;
197
198 if(lv_event_get_code(e) != LV_EVENT_DELETE) return;
199
200 display = (lv_display_t *)lv_event_get_user_data(e);
201 if(display == NULL) return;
202
203 display_ctx = (lv_uefi_display_context_t *)lv_display_get_user_data(display);
204 lv_display_set_user_data(display, NULL);
205
206 if(display_ctx != NULL) _display_ctx_free(display_ctx);
207 }
208
_display_flush_cb(lv_display_t * display,const lv_area_t * area,uint8_t * px_map)209 static void _display_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map)
210 {
211 EFI_STATUS status;
212 int32_t w;
213 int32_t h;
214
215 lv_uefi_display_context_t * display_ctx = (lv_uefi_display_context_t *)lv_display_get_user_data(display);
216 LV_ASSERT_NULL(display_ctx);
217
218 w = (int32_t)area->x2 - (int32_t)area->x1 + 1;
219 h = (int32_t)area->y2 - (int32_t)area->y1 + 1;
220
221 if(w < 0 || h < 0) {
222 LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid rect).");
223 goto error;
224 }
225
226 if((uint32_t)(area->x1 + w) > display_ctx->gop_protocol->Mode->Info->HorizontalResolution) {
227 LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid width).");
228 goto error;
229 }
230
231 if((uint32_t)(area->y1 + h) > display_ctx->gop_protocol->Mode->Info->HorizontalResolution) {
232 LV_LOG_ERROR("[lv_uefi] Invalid lv_display_flush_cb call (invalid height).");
233 goto error;
234 }
235
236 status = display_ctx->gop_protocol->Blt(
237 display_ctx->gop_protocol,
238 (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)px_map,
239 EfiBltBufferToVideo,
240 area->x1,
241 area->y1,
242 area->x1,
243 area->y1,
244 w,
245 h,
246 display_ctx->gop_protocol->Mode->Info->HorizontalResolution * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
247 if(status != EFI_SUCCESS) {
248 LV_LOG_ERROR("[lv_uefi] Blt failed with error code: %llx.", status);
249 goto error;
250 }
251
252 goto finish;
253
254 error:
255
256 finish:
257 lv_display_flush_ready(display);
258 }
259
_display_ctx_free(lv_uefi_display_context_t * display_ctx)260 static void _display_ctx_free(lv_uefi_display_context_t * display_ctx)
261 {
262 if(display_ctx == NULL) {
263 return;
264 }
265
266 if(display_ctx->gop_protocol != NULL) lv_uefi_protocol_close(display_ctx->handle, &_uefi_guid_graphics_output);
267 if(display_ctx->buffer != NULL) lv_free(display_ctx->buffer);
268
269 lv_free(display_ctx);
270 }
271
_display_interface_is_valid(const EFI_GRAPHICS_OUTPUT_PROTOCOL * interface)272 static bool _display_interface_is_valid(const EFI_GRAPHICS_OUTPUT_PROTOCOL * interface)
273 {
274 if(interface == NULL) return FALSE;
275 if(interface->Mode == NULL) return FALSE;
276 if(interface->Mode->Info == NULL) return FALSE;
277 if(interface->Mode->Info->HorizontalResolution == 0) return FALSE;
278 if(interface->Mode->Info->HorizontalResolution >= 32767) return FALSE;
279 if(interface->Mode->Info->VerticalResolution == 0) return FALSE;
280 if(interface->Mode->Info->VerticalResolution >= 32767) return FALSE;
281
282 return TRUE;
283 }
284 #endif