]> git.proxmox.com Git - grub2.git/blame - grub-core/video/efi_gop.c
merge mainline into hints
[grub2.git] / grub-core / video / efi_gop.c
CommitLineData
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
32static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
33static struct grub_efi_gop *gop;
34static unsigned old_mode;
35static int restore_needed;
36
37static 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
45static int
46check_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
55static grub_err_t
56grub_video_gop_init (void)
57{
58 grub_memset (&framebuffer, 0, sizeof(framebuffer));
59 return grub_video_fb_init ();
60}
61
62static grub_err_t
63grub_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
73static int
74grub_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
107static void
108grub_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
129static grub_err_t
130grub_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
186static grub_err_t
187grub_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
325static grub_err_t
326grub_video_gop_swap_buffers (void)
327{
328 /* TODO: Implement buffer swapping. */
329 return GRUB_ERR_NONE;
330}
331
332static grub_err_t
333grub_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
341static grub_err_t
342grub_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
353static struct grub_video_adapter grub_video_gop_adapter =
354 {
355 .name = "EFI GOP driver",
0d90e8a6 356 .id = GRUB_VIDEO_DRIVER_EFI_GOP,
1708050b 357
d49703d1
VS
358 .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE,
359
1708050b
VS
360 .init = grub_video_gop_init,
361 .fini = grub_video_gop_fini,
362 .setup = grub_video_gop_setup,
363 .get_info = grub_video_fb_get_info,
364 .get_info_and_fini = grub_video_gop_get_info_and_fini,
365 .set_palette = grub_video_fb_set_palette,
366 .get_palette = grub_video_fb_get_palette,
367 .set_viewport = grub_video_fb_set_viewport,
368 .get_viewport = grub_video_fb_get_viewport,
369 .map_color = grub_video_fb_map_color,
370 .map_rgb = grub_video_fb_map_rgb,
371 .map_rgba = grub_video_fb_map_rgba,
372 .unmap_color = grub_video_fb_unmap_color,
373 .fill_rect = grub_video_fb_fill_rect,
374 .blit_bitmap = grub_video_fb_blit_bitmap,
375 .blit_render_target = grub_video_fb_blit_render_target,
376 .scroll = grub_video_fb_scroll,
377 .swap_buffers = grub_video_gop_swap_buffers,
378 .create_render_target = grub_video_fb_create_render_target,
379 .delete_render_target = grub_video_fb_delete_render_target,
380 .set_active_render_target = grub_video_gop_set_active_render_target,
381 .get_active_render_target = grub_video_fb_get_active_render_target,
382
383 .next = 0
384 };
385
2f857f98 386GRUB_MOD_INIT(efi_gop)
1708050b
VS
387{
388 if (check_protocol ())
389 grub_video_register (&grub_video_gop_adapter);
390}
391
2f857f98 392GRUB_MOD_FINI(efi_gop)
1708050b
VS
393{
394 if (restore_needed)
395 {
396 efi_call_2 (gop->set_mode, gop, old_mode);
397 restore_needed = 0;
398 }
399 if (gop)
400 grub_video_unregister (&grub_video_gop_adapter);
401}