Commit 45d97a74 authored by Arvind Sankar's avatar Arvind Sankar Committed by Ard Biesheuvel
Browse files

efi/gop: Allow automatically choosing the best mode



Add the ability to automatically pick the highest resolution video mode
(defined as the product of vertical and horizontal resolution) by using
a command-line argument of the form
	video=efifb:auto

If there are multiple modes with the highest resolution, pick one with
the highest color depth.

Signed-off-by: default avatarArvind Sankar <nivedita@alum.mit.edu>
Link: https://lore.kernel.org/r/20200328160601.378299-2-nivedita@alum.mit.edu


Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
parent 9a1663bc
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -57,4 +57,10 @@ mode=n
        "rgb" or "bgr" to match specifically those pixel formats, or a number
        for a mode with matching bits per pixel.

auto
        The EFI stub will choose the mode with the highest resolution (product
        of horizontal and vertical resolution). If there are multiple modes
        with the highest resolution, it will choose one with the highest color
        depth.

Edgar Hucek <gimli@dark-green.com>
+83 −1
Original line number Diff line number Diff line
@@ -18,7 +18,8 @@
enum efi_cmdline_option {
	EFI_CMDLINE_NONE,
	EFI_CMDLINE_MODE_NUM,
	EFI_CMDLINE_RES
	EFI_CMDLINE_RES,
	EFI_CMDLINE_AUTO
};

static struct {
@@ -86,6 +87,19 @@ static bool parse_res(char *option, char **next)
	return true;
}

static bool parse_auto(char *option, char **next)
{
	if (!strstarts(option, "auto"))
		return false;
	option += strlen("auto");
	if (*option && *option++ != ',')
		return false;
	cmdline.option = EFI_CMDLINE_AUTO;

	*next = option;
	return true;
}

void efi_parse_option_graphics(char *option)
{
	while (*option) {
@@ -93,6 +107,8 @@ void efi_parse_option_graphics(char *option)
			continue;
		if (parse_res(option, &option))
			continue;
		if (parse_auto(option, &option))
			continue;

		while (*option && *option++ != ',')
			;
@@ -211,6 +227,69 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
	return cur_mode;
}

static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
{
	efi_status_t status;

	efi_graphics_output_protocol_mode_t *mode;
	efi_graphics_output_mode_info_t *info;
	unsigned long info_size;

	u32 max_mode, cur_mode, best_mode, area;
	u8 depth;
	int pf;
	efi_pixel_bitmask_t pi;
	u32 m, w, h, a;
	u8 d;

	mode = efi_table_attr(gop, mode);

	cur_mode = efi_table_attr(mode, mode);
	max_mode = efi_table_attr(mode, max_mode);

	info = efi_table_attr(mode, info);

	pf = info->pixel_format;
	pi = info->pixel_information;
	w  = info->horizontal_resolution;
	h  = info->vertical_resolution;

	best_mode = cur_mode;
	area = w * h;
	depth = pixel_bpp(pf, pi);

	for (m = 0; m < max_mode; m++) {
		if (m == cur_mode)
			continue;

		status = efi_call_proto(gop, query_mode, m,
					&info_size, &info);
		if (status != EFI_SUCCESS)
			continue;

		pf = info->pixel_format;
		pi = info->pixel_information;
		w  = info->horizontal_resolution;
		h  = info->vertical_resolution;

		efi_bs_call(free_pool, info);

		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
			continue;
		a = w * h;
		if (a < area)
			continue;
		d = pixel_bpp(pf, pi);
		if (a > area || d > depth) {
			best_mode = m;
			area = a;
			depth = d;
		}
	}

	return best_mode;
}

static void set_mode(efi_graphics_output_protocol_t *gop)
{
	efi_graphics_output_protocol_mode_t *mode;
@@ -223,6 +302,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop)
	case EFI_CMDLINE_RES:
		new_mode = choose_mode_res(gop);
		break;
	case EFI_CMDLINE_AUTO:
		new_mode = choose_mode_auto(gop);
		break;
	default:
		return;
	}