2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
19 #define grub_video_render_target grub_video_fbrender_target
22 #include <grub/types.h>
24 #include <grub/misc.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/edid.h>
31 #include <grub/efi/graphics_output.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 static grub_efi_guid_t graphics_output_guid
= GRUB_EFI_GOP_GUID
;
36 static grub_efi_guid_t active_edid_guid
= GRUB_EFI_EDID_ACTIVE_GUID
;
37 static grub_efi_guid_t discovered_edid_guid
= GRUB_EFI_EDID_DISCOVERED_GUID
;
38 static grub_efi_guid_t efi_var_guid
= GRUB_EFI_GLOBAL_VARIABLE_GUID
;
39 static struct grub_efi_gop
*gop
;
40 static unsigned old_mode
;
41 static int restore_needed
;
42 static grub_efi_handle_t gop_handle
;
45 grub_video_gop_iterate (int (*hook
) (const struct grub_video_mode_info
*info
, void *hook_arg
), void *hook_arg
);
49 struct grub_video_mode_info mode_info
;
50 struct grub_video_render_target
*render_target
;
52 grub_uint8_t
*offscreen
;
56 check_protocol_hook (const struct grub_video_mode_info
*info
__attribute__ ((unused
)), void *hook_arg
)
58 int *have_usable_mode
= hook_arg
;
59 *have_usable_mode
= 1;
67 grub_efi_handle_t
*handles
;
68 grub_efi_uintn_t num_handles
, i
;
69 int have_usable_mode
= 0;
71 handles
= grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL
,
72 &graphics_output_guid
, NULL
, &num_handles
);
73 if (!handles
|| num_handles
== 0)
76 for (i
= 0; i
< num_handles
; i
++)
78 gop_handle
= handles
[i
];
79 gop
= grub_efi_open_protocol (gop_handle
, &graphics_output_guid
,
80 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
81 grub_video_gop_iterate (check_protocol_hook
, &have_usable_mode
);
96 grub_video_gop_init (void)
98 grub_memset (&framebuffer
, 0, sizeof(framebuffer
));
99 return grub_video_fb_init ();
103 grub_video_gop_fini (void)
107 efi_call_2 (gop
->set_mode
, gop
, old_mode
);
110 grub_free (framebuffer
.offscreen
);
111 framebuffer
.offscreen
= 0;
112 return grub_video_fb_fini ();
116 grub_video_gop_get_bpp (struct grub_efi_gop_mode_info
*in
)
118 grub_uint32_t total_mask
;
120 switch (in
->pixel_format
)
122 case GRUB_EFI_GOT_BGRA8
:
123 case GRUB_EFI_GOT_RGBA8
:
126 case GRUB_EFI_GOT_BITMASK
:
127 /* Check overlaps. */
128 if ((in
->pixel_bitmask
.r
& in
->pixel_bitmask
.g
)
129 || (in
->pixel_bitmask
.r
& in
->pixel_bitmask
.b
)
130 || (in
->pixel_bitmask
.g
& in
->pixel_bitmask
.b
)
131 || (in
->pixel_bitmask
.r
& in
->pixel_bitmask
.a
)
132 || (in
->pixel_bitmask
.g
& in
->pixel_bitmask
.a
)
133 || (in
->pixel_bitmask
.b
& in
->pixel_bitmask
.a
))
136 total_mask
= in
->pixel_bitmask
.r
| in
->pixel_bitmask
.g
137 | in
->pixel_bitmask
.b
| in
->pixel_bitmask
.a
;
139 for (i
= 31; i
>= 0; i
--)
140 if (total_mask
& (1 << i
))
150 grub_video_gop_get_bitmask (grub_uint32_t mask
, unsigned int *mask_size
,
151 unsigned int *field_pos
)
155 for (i
= 31; i
>= 0; i
--)
160 *mask_size
= *field_pos
= 0;
165 if (!(mask
& (1 << i
)))
168 *mask_size
= last_p
- *field_pos
+ 1;
172 grub_video_gop_fill_real_mode_info (unsigned mode
,
173 struct grub_efi_gop_mode_info
*in
,
174 struct grub_video_mode_info
*out
)
176 out
->mode_number
= mode
;
177 out
->number_of_colors
= 256;
178 out
->width
= in
->width
;
179 out
->height
= in
->height
;
180 out
->mode_type
= GRUB_VIDEO_MODE_TYPE_RGB
;
181 out
->bpp
= grub_video_gop_get_bpp (in
);
182 out
->bytes_per_pixel
= out
->bpp
>> 3;
184 return grub_error (GRUB_ERR_IO
, "unsupported video mode");
185 out
->pitch
= in
->pixels_per_scanline
* out
->bytes_per_pixel
;
187 switch (in
->pixel_format
)
189 case GRUB_EFI_GOT_RGBA8
:
190 out
->red_mask_size
= 8;
191 out
->red_field_pos
= 0;
192 out
->green_mask_size
= 8;
193 out
->green_field_pos
= 8;
194 out
->blue_mask_size
= 8;
195 out
->blue_field_pos
= 16;
196 out
->reserved_mask_size
= 8;
197 out
->reserved_field_pos
= 24;
200 case GRUB_EFI_GOT_BGRA8
:
201 out
->red_mask_size
= 8;
202 out
->red_field_pos
= 16;
203 out
->green_mask_size
= 8;
204 out
->green_field_pos
= 8;
205 out
->blue_mask_size
= 8;
206 out
->blue_field_pos
= 0;
207 out
->reserved_mask_size
= 8;
208 out
->reserved_field_pos
= 24;
211 case GRUB_EFI_GOT_BITMASK
:
212 grub_video_gop_get_bitmask (in
->pixel_bitmask
.r
, &out
->red_mask_size
,
213 &out
->red_field_pos
);
214 grub_video_gop_get_bitmask (in
->pixel_bitmask
.g
, &out
->green_mask_size
,
215 &out
->green_field_pos
);
216 grub_video_gop_get_bitmask (in
->pixel_bitmask
.b
, &out
->blue_mask_size
,
217 &out
->blue_field_pos
);
218 grub_video_gop_get_bitmask (in
->pixel_bitmask
.a
, &out
->reserved_mask_size
,
219 &out
->reserved_field_pos
);
223 return grub_error (GRUB_ERR_IO
, "unsupported video mode");
226 out
->blit_format
= grub_video_get_blit_format (out
);
227 return GRUB_ERR_NONE
;
231 grub_video_gop_fill_mode_info (unsigned mode
,
232 struct grub_efi_gop_mode_info
*in
,
233 struct grub_video_mode_info
*out
)
235 out
->mode_number
= mode
;
236 out
->number_of_colors
= 256;
237 out
->width
= in
->width
;
238 out
->height
= in
->height
;
239 out
->mode_type
= GRUB_VIDEO_MODE_TYPE_RGB
;
240 out
->bytes_per_pixel
= sizeof (struct grub_efi_gop_blt_pixel
);
241 out
->bpp
= out
->bytes_per_pixel
<< 3;
242 out
->pitch
= in
->width
* out
->bytes_per_pixel
;
243 out
->red_mask_size
= 8;
244 out
->red_field_pos
= 16;
245 out
->green_mask_size
= 8;
246 out
->green_field_pos
= 8;
247 out
->blue_mask_size
= 8;
248 out
->blue_field_pos
= 0;
249 out
->reserved_mask_size
= 8;
250 out
->reserved_field_pos
= 24;
252 out
->blit_format
= GRUB_VIDEO_BLIT_FORMAT_BGRA_8888
;
253 out
->mode_type
|= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
254 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP
);
256 return GRUB_ERR_NONE
;
260 grub_video_gop_iterate (int (*hook
) (const struct grub_video_mode_info
*info
, void *hook_arg
), void *hook_arg
)
264 for (mode
= 0; mode
< gop
->mode
->max_mode
; mode
++)
266 grub_efi_uintn_t size
;
267 grub_efi_status_t status
;
268 struct grub_efi_gop_mode_info
*info
= NULL
;
270 struct grub_video_mode_info mode_info
;
272 status
= efi_call_4 (gop
->query_mode
, gop
, mode
, &size
, &info
);
280 err
= grub_video_gop_fill_mode_info (mode
, info
, &mode_info
);
283 grub_errno
= GRUB_ERR_NONE
;
286 if (hook (&mode_info
, hook_arg
))
293 grub_video_gop_get_edid (struct grub_video_edid_info
*edid_info
)
295 struct grub_efi_active_edid
*edid
;
296 grub_size_t copy_size
;
298 grub_memset (edid_info
, 0, sizeof (*edid_info
));
300 edid
= grub_efi_open_protocol (gop_handle
, &active_edid_guid
,
301 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
302 if (!edid
|| edid
->size_of_edid
== 0)
303 edid
= grub_efi_open_protocol (gop_handle
, &discovered_edid_guid
,
304 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
306 if (!edid
|| edid
->size_of_edid
== 0)
308 char edidname
[] = "agp-internal-edid";
309 grub_size_t datasize
;
311 data
= grub_efi_get_variable (edidname
, &efi_var_guid
, &datasize
);
312 if (data
&& datasize
> 16)
314 copy_size
= datasize
- 16;
315 if (copy_size
> sizeof (*edid_info
))
316 copy_size
= sizeof (*edid_info
);
317 grub_memcpy (edid_info
, data
+ 16, copy_size
);
319 return GRUB_ERR_NONE
;
321 return grub_error (GRUB_ERR_BAD_DEVICE
, "EDID information not available");
324 copy_size
= edid
->size_of_edid
;
325 if (copy_size
> sizeof (*edid_info
))
326 copy_size
= sizeof (*edid_info
);
327 grub_memcpy (edid_info
, edid
->edid
, copy_size
);
329 return GRUB_ERR_NONE
;
333 grub_gop_get_preferred_mode (unsigned int *width
, unsigned int *height
)
335 struct grub_video_edid_info edid_info
;
338 err
= grub_video_gop_get_edid (&edid_info
);
341 err
= grub_video_edid_checksum (&edid_info
);
344 err
= grub_video_edid_preferred_mode (&edid_info
, width
, height
);
347 return GRUB_ERR_NONE
;
351 grub_video_gop_setup (unsigned int width
, unsigned int height
,
352 unsigned int mode_type
,
353 unsigned int mode_mask
__attribute__ ((unused
)))
356 struct grub_efi_gop_mode_info
*info
= NULL
;
357 unsigned best_mode
= 0;
361 unsigned long long best_volume
= 0;
362 unsigned int preferred_width
= 0, preferred_height
= 0;
363 grub_uint8_t
*buffer
;
365 depth
= (mode_type
& GRUB_VIDEO_MODE_TYPE_DEPTH_MASK
)
366 >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS
;
368 if (width
== 0 && height
== 0)
370 err
= grub_gop_get_preferred_mode (&preferred_width
, &preferred_height
);
371 if (err
|| preferred_width
>= 4096 || preferred_height
>= 4096)
373 preferred_width
= 800;
374 preferred_height
= 600;
375 grub_errno
= GRUB_ERR_NONE
;
379 /* Keep current mode if possible. */
382 bpp
= grub_video_gop_get_bpp (gop
->mode
->info
);
383 if (bpp
&& ((width
== gop
->mode
->info
->width
384 && height
== gop
->mode
->info
->height
)
385 || (width
== 0 && height
== 0))
386 && (depth
== bpp
|| depth
== 0))
388 grub_dprintf ("video", "GOP: keeping mode %d\n", gop
->mode
->mode
);
389 best_mode
= gop
->mode
->mode
;
397 grub_dprintf ("video", "GOP: %d modes detected\n", gop
->mode
->max_mode
);
398 for (mode
= 0; mode
< gop
->mode
->max_mode
; mode
++)
400 grub_efi_uintn_t size
;
401 grub_efi_status_t status
;
403 status
= efi_call_4 (gop
->query_mode
, gop
, mode
, &size
, &info
);
410 grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode
, info
->width
,
413 if (preferred_width
&& (info
->width
> preferred_width
414 || info
->height
> preferred_height
))
416 grub_dprintf ("video", "GOP: mode %d: too large\n", mode
);
420 bpp
= grub_video_gop_get_bpp (info
);
423 grub_dprintf ("video", "GOP: mode %d: incompatible pixel mode\n",
428 grub_dprintf ("video", "GOP: mode %d: depth %d\n", mode
, bpp
);
430 if (!(((info
->width
== width
&& info
->height
== height
)
431 || (width
== 0 && height
== 0))
432 && (bpp
== depth
|| depth
== 0)))
434 grub_dprintf ("video", "GOP: mode %d: rejected\n", mode
);
438 if (best_volume
< ((unsigned long long) info
->width
)
439 * ((unsigned long long) info
->height
)
440 * ((unsigned long long) bpp
))
442 best_volume
= ((unsigned long long) info
->width
)
443 * ((unsigned long long) info
->height
)
444 * ((unsigned long long) bpp
);
453 grub_dprintf ("video", "GOP: no mode found\n");
454 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no matching mode found");
457 if (best_mode
!= gop
->mode
->mode
)
461 old_mode
= gop
->mode
->mode
;
464 efi_call_2 (gop
->set_mode
, gop
, best_mode
);
467 info
= gop
->mode
->info
;
469 err
= grub_video_gop_fill_mode_info (gop
->mode
->mode
, info
,
470 &framebuffer
.mode_info
);
473 grub_dprintf ("video", "GOP: couldn't fill mode info\n");
477 framebuffer
.ptr
= (void *) (grub_addr_t
) gop
->mode
->fb_base
;
478 framebuffer
.offscreen
479 = grub_malloc (framebuffer
.mode_info
.height
480 * framebuffer
.mode_info
.width
481 * sizeof (struct grub_efi_gop_blt_pixel
));
483 buffer
= framebuffer
.offscreen
;
487 grub_dprintf ("video", "GOP: couldn't allocate shadow\n");
489 err
= grub_video_gop_fill_mode_info (gop
->mode
->mode
, info
,
490 &framebuffer
.mode_info
);
491 buffer
= framebuffer
.ptr
;
494 grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n",
495 framebuffer
.ptr
, framebuffer
.mode_info
.width
,
496 framebuffer
.mode_info
.height
, framebuffer
.mode_info
.bpp
);
498 err
= grub_video_fb_create_render_target_from_pointer
499 (&framebuffer
.render_target
, &framebuffer
.mode_info
, buffer
);
503 grub_dprintf ("video", "GOP: Couldn't create FB target\n");
507 err
= grub_video_fb_set_active_render_target (framebuffer
.render_target
);
511 grub_dprintf ("video", "GOP: Couldn't set FB target\n");
515 err
= grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS
,
516 grub_video_fbstd_colors
);
519 grub_dprintf ("video", "GOP: Couldn't set palette\n");
521 grub_dprintf ("video", "GOP: Success\n");
527 grub_video_gop_swap_buffers (void)
529 if (framebuffer
.offscreen
)
531 efi_call_10 (gop
->blt
, gop
, framebuffer
.offscreen
,
532 GRUB_EFI_BLT_BUFFER_TO_VIDEO
, 0, 0, 0, 0,
533 framebuffer
.mode_info
.width
, framebuffer
.mode_info
.height
,
534 framebuffer
.mode_info
.width
* 4);
536 return GRUB_ERR_NONE
;
540 grub_video_gop_set_active_render_target (struct grub_video_render_target
*target
)
542 if (target
== GRUB_VIDEO_RENDER_TARGET_DISPLAY
)
543 target
= framebuffer
.render_target
;
545 return grub_video_fb_set_active_render_target (target
);
549 grub_video_gop_get_info_and_fini (struct grub_video_mode_info
*mode_info
,
554 err
= grub_video_gop_fill_real_mode_info (gop
->mode
->mode
, gop
->mode
->info
,
558 grub_dprintf ("video", "GOP: couldn't fill mode info\n");
562 *framebuf
= (char *) framebuffer
.ptr
;
564 grub_video_fb_fini ();
566 grub_free (framebuffer
.offscreen
);
567 framebuffer
.offscreen
= 0;
569 return GRUB_ERR_NONE
;
572 static struct grub_video_adapter grub_video_gop_adapter
=
574 .name
= "EFI GOP driver",
575 .id
= GRUB_VIDEO_DRIVER_EFI_GOP
,
577 .prio
= GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE
,
579 .init
= grub_video_gop_init
,
580 .fini
= grub_video_gop_fini
,
581 .setup
= grub_video_gop_setup
,
582 .get_info
= grub_video_fb_get_info
,
583 .get_info_and_fini
= grub_video_gop_get_info_and_fini
,
584 .get_edid
= grub_video_gop_get_edid
,
585 .set_palette
= grub_video_fb_set_palette
,
586 .get_palette
= grub_video_fb_get_palette
,
587 .set_viewport
= grub_video_fb_set_viewport
,
588 .get_viewport
= grub_video_fb_get_viewport
,
589 .set_region
= grub_video_fb_set_region
,
590 .get_region
= grub_video_fb_get_region
,
591 .set_area_status
= grub_video_fb_set_area_status
,
592 .get_area_status
= grub_video_fb_get_area_status
,
593 .map_color
= grub_video_fb_map_color
,
594 .map_rgb
= grub_video_fb_map_rgb
,
595 .map_rgba
= grub_video_fb_map_rgba
,
596 .unmap_color
= grub_video_fb_unmap_color
,
597 .fill_rect
= grub_video_fb_fill_rect
,
598 .blit_bitmap
= grub_video_fb_blit_bitmap
,
599 .blit_render_target
= grub_video_fb_blit_render_target
,
600 .scroll
= grub_video_fb_scroll
,
601 .swap_buffers
= grub_video_gop_swap_buffers
,
602 .create_render_target
= grub_video_fb_create_render_target
,
603 .delete_render_target
= grub_video_fb_delete_render_target
,
604 .set_active_render_target
= grub_video_gop_set_active_render_target
,
605 .get_active_render_target
= grub_video_fb_get_active_render_target
,
606 .iterate
= grub_video_gop_iterate
,
611 GRUB_MOD_INIT(efi_gop
)
613 if (check_protocol ())
614 grub_video_register (&grub_video_gop_adapter
);
617 GRUB_MOD_FINI(efi_gop
)
621 efi_call_2 (gop
->set_mode
, gop
, old_mode
);
625 grub_video_unregister (&grub_video_gop_adapter
);