Use EDID on EFI.

* grub-core/kern/efi/efi.c (grub_efi_get_variable): New argument
	datasize_out.
	* grub-core/video/efi_gop.c (check_protocol): Check that GOP has usable
	modes. Set gop_handle.
	(grub_video_gop_get_edid): New function.
	(grub_gop_get_preferred_mode): Likewise.
	(grub_video_gop_setup): Use grub_gop_get_preferred_mode.
	(grub_video_efi_gop_adapter): Set .get_edid.
	* include/grub/efi/edid.h: New file.
	* include/grub/efi/efi.h (grub_efi_get_variable): Update proto.

	Also-By: Vladimir Serbinenko <phcoder@gmail.com>
This commit is contained in:
Matthew Garrett 2012-03-04 00:48:21 +01:00 committed by Vladimir 'phcoder' Serbinenko
parent 32107ec02a
commit 3935dde2f2
5 changed files with 200 additions and 5 deletions

View file

@ -183,7 +183,8 @@ grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
}
void *
grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
grub_size_t *datasize_out)
{
grub_efi_status_t status;
grub_efi_uintn_t datasize = 0;
@ -192,6 +193,8 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
void *data;
grub_size_t len, len16;
*datasize_out = 0;
len = grub_strlen (var);
len16 = len * GRUB_MAX_UTF16_PER_UTF8;
var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
@ -204,6 +207,9 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
status = efi_call_5 (r->get_variable, var16, guid, NULL, &datasize, NULL);
if (!datasize)
return NULL;
data = grub_malloc (datasize);
if (!data)
{
@ -215,7 +221,10 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
grub_free (var16);
if (status == GRUB_EFI_SUCCESS)
return data;
{
*datasize_out = datasize;
return data;
}
grub_free (data);
return NULL;

View file

@ -27,14 +27,22 @@
#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
{
@ -47,9 +55,37 @@ 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;
}
@ -220,6 +256,64 @@ grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info))
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,
@ -232,10 +326,23 @@ grub_video_gop_setup (unsigned int width, unsigned int height,
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)
{
@ -270,6 +377,13 @@ grub_video_gop_setup (unsigned int width, unsigned int height,
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)
{
@ -401,6 +515,7 @@ static struct grub_video_adapter grub_video_gop_adapter =
.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,