]>
Commit | Line | Data |
---|---|---|
1708050b VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #define grub_video_render_target grub_video_fbrender_target | |
20 | ||
21 | #include <grub/err.h> | |
22 | #include <grub/types.h> | |
23 | #include <grub/dl.h> | |
24 | #include <grub/misc.h> | |
25 | #include <grub/mm.h> | |
26 | #include <grub/video.h> | |
27 | #include <grub/video_fb.h> | |
28 | #include <grub/efi/api.h> | |
29 | #include <grub/efi/efi.h> | |
30 | #include <grub/efi/graphics_output.h> | |
31 | ||
32 | static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID; | |
33 | static struct grub_efi_gop *gop; | |
34 | static unsigned old_mode; | |
35 | static int restore_needed; | |
36 | ||
37 | static struct | |
38 | { | |
39 | struct grub_video_mode_info mode_info; | |
40 | struct grub_video_render_target *render_target; | |
41 | grub_uint8_t *ptr; | |
42 | } framebuffer; | |
43 | ||
44 | ||
45 | static int | |
46 | check_protocol (void) | |
47 | { | |
48 | gop = grub_efi_locate_protocol (&graphics_output_guid, 0); | |
49 | if (gop) | |
50 | return 1; | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
55 | static grub_err_t | |
56 | grub_video_gop_init (void) | |
57 | { | |
58 | grub_memset (&framebuffer, 0, sizeof(framebuffer)); | |
59 | return grub_video_fb_init (); | |
60 | } | |
61 | ||
62 | static grub_err_t | |
63 | grub_video_gop_fini (void) | |
64 | { | |
65 | if (restore_needed) | |
66 | { | |
67 | efi_call_2 (gop->set_mode, gop, old_mode); | |
68 | restore_needed = 0; | |
69 | } | |
70 | return grub_video_fb_fini (); | |
71 | } | |
72 | ||
73 | static int | |
74 | grub_video_gop_get_bpp (struct grub_efi_gop_mode_info *in) | |
75 | { | |
76 | grub_uint32_t total_mask; | |
77 | int i; | |
78 | switch (in->pixel_format) | |
79 | { | |
80 | case GRUB_EFI_GOT_BGRA8: | |
81 | case GRUB_EFI_GOT_RGBA8: | |
82 | return 32; | |
83 | ||
84 | case GRUB_EFI_GOT_BITMASK: | |
85 | /* Check overlaps. */ | |
86 | if ((in->pixel_bitmask.r & in->pixel_bitmask.g) | |
87 | || (in->pixel_bitmask.r & in->pixel_bitmask.b) | |
88 | || (in->pixel_bitmask.g & in->pixel_bitmask.b) | |
89 | || (in->pixel_bitmask.r & in->pixel_bitmask.a) | |
90 | || (in->pixel_bitmask.g & in->pixel_bitmask.a) | |
91 | || (in->pixel_bitmask.b & in->pixel_bitmask.a)) | |
92 | return 0; | |
93 | ||
94 | total_mask = in->pixel_bitmask.r | in->pixel_bitmask.g | |
95 | | in->pixel_bitmask.b | in->pixel_bitmask.a; | |
a2c1332b | 96 | |
1708050b VS |
97 | for (i = 31; i >= 0; i--) |
98 | if (total_mask & (1 << i)) | |
99 | return i + 1; | |
100 | ||
101 | /* Fall through. */ | |
102 | default: | |
103 | return 0; | |
104 | } | |
105 | } | |
106 | ||
107 | static void | |
108 | grub_video_gop_get_bitmask (grub_uint32_t mask, unsigned int *mask_size, | |
109 | unsigned int *field_pos) | |
110 | { | |
111 | int i; | |
112 | int last_p; | |
113 | for (i = 31; i >= 0; i--) | |
114 | if (mask & (1 << i)) | |
115 | break; | |
116 | if (i == -1) | |
117 | { | |
118 | *mask_size = *field_pos = 0; | |
119 | return; | |
120 | } | |
121 | last_p = i; | |
122 | for (; i >= 0; i--) | |
123 | if (!(mask & (1 << i))) | |
124 | break; | |
125 | *field_pos = i + 1; | |
009ec743 | 126 | *mask_size = last_p - *field_pos + 1; |
1708050b VS |
127 | } |
128 | ||
129 | static grub_err_t | |
130 | grub_video_gop_fill_mode_info (struct grub_efi_gop_mode_info *in, | |
131 | struct grub_video_mode_info *out) | |
132 | { | |
133 | out->number_of_colors = 256; | |
134 | out->width = in->width; | |
135 | out->height = in->height; | |
136 | out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; | |
137 | out->bpp = grub_video_gop_get_bpp (in); | |
138 | out->bytes_per_pixel = out->bpp >> 3; | |
139 | if (!out->bpp) | |
7fd0baee | 140 | return grub_error (GRUB_ERR_IO, "unsupported video mode"); |
1708050b VS |
141 | out->pitch = in->pixels_per_scanline * out->bytes_per_pixel; |
142 | ||
143 | switch (in->pixel_format) | |
144 | { | |
145 | case GRUB_EFI_GOT_RGBA8: | |
146 | out->red_mask_size = 8; | |
147 | out->red_field_pos = 0; | |
148 | out->green_mask_size = 8; | |
149 | out->green_field_pos = 8; | |
150 | out->blue_mask_size = 8; | |
151 | out->blue_field_pos = 16; | |
152 | out->reserved_mask_size = 8; | |
153 | out->reserved_field_pos = 24; | |
154 | break; | |
155 | ||
156 | case GRUB_EFI_GOT_BGRA8: | |
157 | out->red_mask_size = 8; | |
158 | out->red_field_pos = 16; | |
159 | out->green_mask_size = 8; | |
160 | out->green_field_pos = 8; | |
161 | out->blue_mask_size = 8; | |
162 | out->blue_field_pos = 0; | |
163 | out->reserved_mask_size = 8; | |
164 | out->reserved_field_pos = 24; | |
165 | break; | |
166 | ||
167 | case GRUB_EFI_GOT_BITMASK: | |
168 | grub_video_gop_get_bitmask (in->pixel_bitmask.r, &out->red_mask_size, | |
169 | &out->red_field_pos); | |
170 | grub_video_gop_get_bitmask (in->pixel_bitmask.g, &out->green_mask_size, | |
171 | &out->green_field_pos); | |
172 | grub_video_gop_get_bitmask (in->pixel_bitmask.b, &out->blue_mask_size, | |
173 | &out->blue_field_pos); | |
174 | grub_video_gop_get_bitmask (in->pixel_bitmask.a, &out->reserved_mask_size, | |
175 | &out->reserved_field_pos); | |
176 | break; | |
177 | ||
178 | default: | |
7fd0baee | 179 | return grub_error (GRUB_ERR_IO, "unsupported video mode"); |
1708050b VS |
180 | } |
181 | ||
182 | out->blit_format = grub_video_get_blit_format (out); | |
183 | return GRUB_ERR_NONE; | |
184 | } | |
185 | ||
186 | static grub_err_t | |
187 | grub_video_gop_setup (unsigned int width, unsigned int height, | |
e6d8d32a | 188 | unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) |
1708050b VS |
189 | { |
190 | unsigned int depth; | |
191 | struct grub_efi_gop_mode_info *info = NULL; | |
192 | unsigned best_mode = 0; | |
193 | grub_err_t err; | |
194 | unsigned bpp; | |
195 | int found = 0; | |
196 | unsigned long long best_volume = 0; | |
197 | ||
198 | depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) | |
199 | >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; | |
200 | ||
201 | /* Keep current mode if possible. */ | |
202 | if (gop->mode->info) | |
203 | { | |
204 | bpp = grub_video_gop_get_bpp (gop->mode->info); | |
205 | if (bpp && ((width == gop->mode->info->width | |
206 | && height == gop->mode->info->height) | |
207 | || (width == 0 && height == 0)) | |
208 | && (depth == bpp || depth == 0)) | |
209 | { | |
210 | grub_dprintf ("video", "GOP: keeping mode %d\n", gop->mode->mode); | |
211 | best_mode = gop->mode->mode; | |
212 | found = 1; | |
213 | } | |
214 | } | |
a2c1332b | 215 | |
1708050b VS |
216 | if (!found) |
217 | { | |
218 | unsigned mode; | |
219 | grub_dprintf ("video", "GOP: %d modes detected\n", gop->mode->max_mode); | |
220 | for (mode = 0; mode < gop->mode->max_mode; mode++) | |
221 | { | |
222 | grub_efi_uintn_t size; | |
223 | grub_efi_status_t status; | |
a2c1332b | 224 | |
1708050b VS |
225 | status = efi_call_4 (gop->query_mode, gop, mode, &size, &info); |
226 | if (status) | |
227 | { | |
228 | info = 0; | |
229 | break; | |
230 | } | |
231 | ||
232 | grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width, | |
233 | info->height); | |
234 | ||
235 | bpp = grub_video_gop_get_bpp (info); | |
236 | if (!bpp) | |
237 | { | |
238 | grub_dprintf ("video", "GOP: mode %d: incompatible pixel mode\n", | |
239 | mode); | |
240 | continue; | |
241 | } | |
242 | ||
243 | grub_dprintf ("video", "GOP: mode %d: depth %d\n", mode, bpp); | |
244 | ||
245 | if (!(((info->width == width && info->height == height) | |
246 | || (width == 0 && height == 0)) | |
247 | && (bpp == depth || depth == 0))) | |
248 | { | |
249 | grub_dprintf ("video", "GOP: mode %d: rejected\n", mode); | |
250 | continue; | |
251 | } | |
252 | ||
253 | if (best_volume < ((unsigned long long) info->width) | |
254 | * ((unsigned long long) info->height) | |
255 | * ((unsigned long long) bpp)) | |
256 | { | |
257 | best_volume = ((unsigned long long) info->width) | |
a2c1332b | 258 | * ((unsigned long long) info->height) |
1708050b VS |
259 | * ((unsigned long long) bpp); |
260 | best_mode = mode; | |
261 | } | |
262 | found = 1; | |
263 | } | |
264 | } | |
265 | ||
266 | if (!found) | |
267 | { | |
268 | grub_dprintf ("video", "GOP: no mode found\n"); | |
7fd0baee | 269 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); |
1708050b VS |
270 | } |
271 | ||
272 | if (best_mode != gop->mode->mode) | |
273 | { | |
274 | if (!restore_needed) | |
275 | { | |
276 | old_mode = gop->mode->mode; | |
277 | restore_needed = 1; | |
278 | } | |
279 | efi_call_2 (gop->set_mode, gop, best_mode); | |
280 | } | |
281 | ||
282 | info = gop->mode->info; | |
283 | ||
284 | err = grub_video_gop_fill_mode_info (info, &framebuffer.mode_info); | |
285 | if (err) | |
286 | { | |
287 | grub_dprintf ("video", "GOP: couldn't fill mode info\n"); | |
288 | return err; | |
289 | } | |
290 | ||
f704cae3 | 291 | framebuffer.ptr = (void *) (grub_addr_t) gop->mode->fb_base; |
1708050b VS |
292 | |
293 | grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n", | |
294 | framebuffer.ptr, framebuffer.mode_info.width, | |
295 | framebuffer.mode_info.height, framebuffer.mode_info.bpp); | |
a2c1332b FZ |
296 | |
297 | err = grub_video_fb_create_render_target_from_pointer | |
1708050b VS |
298 | (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr); |
299 | ||
300 | if (err) | |
301 | { | |
302 | grub_dprintf ("video", "GOP: Couldn't create FB target\n"); | |
303 | return err; | |
304 | } | |
a2c1332b | 305 | |
1708050b | 306 | err = grub_video_fb_set_active_render_target (framebuffer.render_target); |
a2c1332b | 307 | |
1708050b VS |
308 | if (err) |
309 | { | |
310 | grub_dprintf ("video", "GOP: Couldn't set FB target\n"); | |
311 | return err; | |
312 | } | |
a2c1332b | 313 | |
1708050b VS |
314 | err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, |
315 | grub_video_fbstd_colors); | |
316 | ||
317 | if (err) | |
318 | grub_dprintf ("video", "GOP: Couldn't set palette\n"); | |
319 | else | |
320 | grub_dprintf ("video", "GOP: Success\n"); | |
a2c1332b | 321 | |
1708050b VS |
322 | return err; |
323 | } | |
324 | ||
325 | static grub_err_t | |
326 | grub_video_gop_swap_buffers (void) | |
327 | { | |
328 | /* TODO: Implement buffer swapping. */ | |
329 | return GRUB_ERR_NONE; | |
330 | } | |
331 | ||
332 | static grub_err_t | |
333 | grub_video_gop_set_active_render_target (struct grub_video_render_target *target) | |
334 | { | |
335 | if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) | |
336 | target = framebuffer.render_target; | |
337 | ||
338 | return grub_video_fb_set_active_render_target (target); | |
339 | } | |
340 | ||
341 | static grub_err_t | |
342 | grub_video_gop_get_info_and_fini (struct grub_video_mode_info *mode_info, | |
343 | void **framebuf) | |
344 | { | |
345 | grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); | |
346 | *framebuf = (char *) framebuffer.ptr; | |
347 | ||
348 | grub_video_fb_fini (); | |
349 | ||
350 | return GRUB_ERR_NONE; | |
351 | } | |
352 | ||
353 | static struct grub_video_adapter grub_video_gop_adapter = | |
354 | { | |
355 | .name = "EFI GOP driver", | |
0d90e8a6 | 356 | .id = GRUB_VIDEO_DRIVER_EFI_GOP, |
1708050b VS |
357 | |
358 | .init = grub_video_gop_init, | |
359 | .fini = grub_video_gop_fini, | |
360 | .setup = grub_video_gop_setup, | |
361 | .get_info = grub_video_fb_get_info, | |
362 | .get_info_and_fini = grub_video_gop_get_info_and_fini, | |
363 | .set_palette = grub_video_fb_set_palette, | |
364 | .get_palette = grub_video_fb_get_palette, | |
365 | .set_viewport = grub_video_fb_set_viewport, | |
366 | .get_viewport = grub_video_fb_get_viewport, | |
367 | .map_color = grub_video_fb_map_color, | |
368 | .map_rgb = grub_video_fb_map_rgb, | |
369 | .map_rgba = grub_video_fb_map_rgba, | |
370 | .unmap_color = grub_video_fb_unmap_color, | |
371 | .fill_rect = grub_video_fb_fill_rect, | |
372 | .blit_bitmap = grub_video_fb_blit_bitmap, | |
373 | .blit_render_target = grub_video_fb_blit_render_target, | |
374 | .scroll = grub_video_fb_scroll, | |
375 | .swap_buffers = grub_video_gop_swap_buffers, | |
376 | .create_render_target = grub_video_fb_create_render_target, | |
377 | .delete_render_target = grub_video_fb_delete_render_target, | |
378 | .set_active_render_target = grub_video_gop_set_active_render_target, | |
379 | .get_active_render_target = grub_video_fb_get_active_render_target, | |
380 | ||
381 | .next = 0 | |
382 | }; | |
383 | ||
2f857f98 | 384 | GRUB_MOD_INIT(efi_gop) |
1708050b VS |
385 | { |
386 | if (check_protocol ()) | |
387 | grub_video_register (&grub_video_gop_adapter); | |
388 | } | |
389 | ||
2f857f98 | 390 | GRUB_MOD_FINI(efi_gop) |
1708050b VS |
391 | { |
392 | if (restore_needed) | |
393 | { | |
394 | efi_call_2 (gop->set_mode, gop, old_mode); | |
395 | restore_needed = 0; | |
396 | } | |
397 | if (gop) | |
398 | grub_video_unregister (&grub_video_gop_adapter); | |
399 | } |