#include <grub/video_fb.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
+#include <grub/efi/edid.h>
#include <grub/efi/graphics_output.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
+static grub_efi_guid_t active_edid_guid = GRUB_EFI_EDID_ACTIVE_GUID;
+static grub_efi_guid_t discovered_edid_guid = GRUB_EFI_EDID_DISCOVERED_GUID;
+static grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
static struct grub_efi_gop *gop;
static unsigned old_mode;
static int restore_needed;
+static grub_efi_handle_t gop_handle;
+
+static int
+grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info));
static struct
{
static int
check_protocol (void)
{
- gop = grub_efi_locate_protocol (&graphics_output_guid, 0);
- if (gop)
+ grub_efi_handle_t *handles;
+ grub_efi_uintn_t num_handles, i;
+ int have_usable_mode = 0;
+
+ auto int hook (const struct grub_video_mode_info *info);
+ int hook (const struct grub_video_mode_info *info __attribute__ ((unused)))
+ {
+ have_usable_mode = 1;
return 1;
+ }
+
+ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
+ &graphics_output_guid, NULL, &num_handles);
+ if (!handles || num_handles == 0)
+ return 0;
+
+ for (i = 0; i < num_handles; i++)
+ {
+ gop_handle = handles[i];
+ gop = grub_efi_open_protocol (gop_handle, &graphics_output_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ grub_video_gop_iterate (hook);
+ if (have_usable_mode)
+ {
+ grub_free (handles);
+ return 1;
+ }
+ }
+
+ gop = 0;
+ gop_handle = 0;
return 0;
}
return 0;
}
+static grub_err_t
+grub_video_gop_get_edid (struct grub_video_edid_info *edid_info)
+{
+ struct grub_efi_active_edid *edid;
+ grub_size_t copy_size;
+
+ grub_memset (edid_info, 0, sizeof (*edid_info));
+
+ edid = grub_efi_open_protocol (gop_handle, &active_edid_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (!edid || edid->size_of_edid == 0)
+ edid = grub_efi_open_protocol (gop_handle, &discovered_edid_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (!edid || edid->size_of_edid == 0)
+ {
+ char edidname[] = "agp-internal-edid";
+ grub_size_t datasize;
+ grub_uint8_t *data;
+ data = grub_efi_get_variable (edidname, &efi_var_guid, &datasize);
+ if (data && datasize > 16)
+ {
+ copy_size = datasize - 16;
+ if (copy_size > sizeof (*edid_info))
+ copy_size = sizeof (*edid_info);
+ grub_memcpy (edid_info, data + 16, copy_size);
+ grub_free (data);
+ return GRUB_ERR_NONE;
+ }
+ return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available");
+ }
+
+ copy_size = edid->size_of_edid;
+ if (copy_size > sizeof (*edid_info))
+ copy_size = sizeof (*edid_info);
+ grub_memcpy (edid_info, edid->edid, copy_size);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_gop_get_preferred_mode (unsigned int *width, unsigned int *height)
+{
+ struct grub_video_edid_info edid_info;
+ grub_err_t err;
+
+ err = grub_video_gop_get_edid (&edid_info);
+ if (err)
+ return err;
+ err = grub_video_edid_checksum (&edid_info);
+ if (err)
+ return err;
+ err = grub_video_edid_preferred_mode (&edid_info, width, height);
+ if (err)
+ return err;
+ return GRUB_ERR_NONE;
+}
+
static grub_err_t
grub_video_gop_setup (unsigned int width, unsigned int height,
unsigned int mode_type,
unsigned bpp;
int found = 0;
unsigned long long best_volume = 0;
+ unsigned int preferred_width = 0, preferred_height = 0;
depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
>> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
+ if (width == 0 && height == 0)
+ {
+ err = 1;
+ grub_gop_get_preferred_mode (&preferred_width, &preferred_height);
+ if (err)
+ {
+ preferred_width = 800;
+ preferred_height = 600;
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+
/* Keep current mode if possible. */
if (gop->mode->info)
{
grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width,
info->height);
+ if (preferred_width && (info->width > preferred_width
+ || info->height > preferred_height))
+ {
+ grub_dprintf ("video", "GOP: mode %d: too large\n", mode);
+ continue;
+ }
+
bpp = grub_video_gop_get_bpp (info);
if (!bpp)
{
.setup = grub_video_gop_setup,
.get_info = grub_video_fb_get_info,
.get_info_and_fini = grub_video_gop_get_info_and_fini,
+ .get_edid = grub_video_gop_get_edid,
.set_palette = grub_video_fb_set_palette,
.get_palette = grub_video_fb_get_palette,
.set_viewport = grub_video_fb_set_viewport,
--- /dev/null
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_EFI_EDID_HEADER
+#define GRUB_EFI_EDID_HEADER 1
+
+/* Based on UEFI specification. */
+
+#define GRUB_EFI_EDID_ACTIVE_GUID \
+ { 0xbd8c1056, 0x9f36, 0x44ec, { 0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86 }}
+
+#define GRUB_EFI_EDID_DISCOVERED_GUID \
+ {0x1c0c34f6,0xd380,0x41fa, {0xa0,0x49,0x8a,0xd0,0x6c,0x1a,0x66,0xaa}}
+
+#define GRUB_EFI_EDID_OVERRIDE_GUID \
+ {0x48ecb431,0xfb72,0x45c0, {0xa9,0x22,0xf4,0x58,0xfe,0x4,0xb,0xd5}}
+
+struct grub_efi_edid_override;
+
+typedef grub_efi_status_t
+(*grub_efi_edid_override_get_edid) (struct grub_efi_edid_override *this,
+ grub_efi_handle_t *childhandle,
+ grub_efi_uint32_t *attributes,
+ grub_efi_uintn_t *edidsize,
+ grub_efi_uint8_t *edid);
+struct grub_efi_edid_override {
+ grub_efi_edid_override_get_edid get_edid;
+};
+
+typedef struct grub_efi_edid_override grub_efi_edid_override_t;
+
+
+struct grub_efi_active_edid
+{
+ grub_uint32_t size_of_edid;
+ grub_uint8_t *edid;
+};
+
+#endif