18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright 2011 Intel Corporation; author Matt Fleming
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * ----------------------------------------------------------------------- */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/bitops.h>
98c2ecf20Sopenharmony_ci#include <linux/ctype.h>
108c2ecf20Sopenharmony_ci#include <linux/efi.h>
118c2ecf20Sopenharmony_ci#include <linux/screen_info.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <asm/efi.h>
148c2ecf20Sopenharmony_ci#include <asm/setup.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "efistub.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cienum efi_cmdline_option {
198c2ecf20Sopenharmony_ci	EFI_CMDLINE_NONE,
208c2ecf20Sopenharmony_ci	EFI_CMDLINE_MODE_NUM,
218c2ecf20Sopenharmony_ci	EFI_CMDLINE_RES,
228c2ecf20Sopenharmony_ci	EFI_CMDLINE_AUTO,
238c2ecf20Sopenharmony_ci	EFI_CMDLINE_LIST
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct {
278c2ecf20Sopenharmony_ci	enum efi_cmdline_option option;
288c2ecf20Sopenharmony_ci	union {
298c2ecf20Sopenharmony_ci		u32 mode;
308c2ecf20Sopenharmony_ci		struct {
318c2ecf20Sopenharmony_ci			u32 width, height;
328c2ecf20Sopenharmony_ci			int format;
338c2ecf20Sopenharmony_ci			u8 depth;
348c2ecf20Sopenharmony_ci		} res;
358c2ecf20Sopenharmony_ci	};
368c2ecf20Sopenharmony_ci} cmdline = { .option = EFI_CMDLINE_NONE };
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic bool parse_modenum(char *option, char **next)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	u32 m;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!strstarts(option, "mode="))
438c2ecf20Sopenharmony_ci		return false;
448c2ecf20Sopenharmony_ci	option += strlen("mode=");
458c2ecf20Sopenharmony_ci	m = simple_strtoull(option, &option, 0);
468c2ecf20Sopenharmony_ci	if (*option && *option++ != ',')
478c2ecf20Sopenharmony_ci		return false;
488c2ecf20Sopenharmony_ci	cmdline.option = EFI_CMDLINE_MODE_NUM;
498c2ecf20Sopenharmony_ci	cmdline.mode   = m;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	*next = option;
528c2ecf20Sopenharmony_ci	return true;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic bool parse_res(char *option, char **next)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	u32 w, h, d = 0;
588c2ecf20Sopenharmony_ci	int pf = -1;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (!isdigit(*option))
618c2ecf20Sopenharmony_ci		return false;
628c2ecf20Sopenharmony_ci	w = simple_strtoull(option, &option, 10);
638c2ecf20Sopenharmony_ci	if (*option++ != 'x' || !isdigit(*option))
648c2ecf20Sopenharmony_ci		return false;
658c2ecf20Sopenharmony_ci	h = simple_strtoull(option, &option, 10);
668c2ecf20Sopenharmony_ci	if (*option == '-') {
678c2ecf20Sopenharmony_ci		option++;
688c2ecf20Sopenharmony_ci		if (strstarts(option, "rgb")) {
698c2ecf20Sopenharmony_ci			option += strlen("rgb");
708c2ecf20Sopenharmony_ci			pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
718c2ecf20Sopenharmony_ci		} else if (strstarts(option, "bgr")) {
728c2ecf20Sopenharmony_ci			option += strlen("bgr");
738c2ecf20Sopenharmony_ci			pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
748c2ecf20Sopenharmony_ci		} else if (isdigit(*option))
758c2ecf20Sopenharmony_ci			d = simple_strtoull(option, &option, 10);
768c2ecf20Sopenharmony_ci		else
778c2ecf20Sopenharmony_ci			return false;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	if (*option && *option++ != ',')
808c2ecf20Sopenharmony_ci		return false;
818c2ecf20Sopenharmony_ci	cmdline.option     = EFI_CMDLINE_RES;
828c2ecf20Sopenharmony_ci	cmdline.res.width  = w;
838c2ecf20Sopenharmony_ci	cmdline.res.height = h;
848c2ecf20Sopenharmony_ci	cmdline.res.format = pf;
858c2ecf20Sopenharmony_ci	cmdline.res.depth  = d;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	*next = option;
888c2ecf20Sopenharmony_ci	return true;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic bool parse_auto(char *option, char **next)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	if (!strstarts(option, "auto"))
948c2ecf20Sopenharmony_ci		return false;
958c2ecf20Sopenharmony_ci	option += strlen("auto");
968c2ecf20Sopenharmony_ci	if (*option && *option++ != ',')
978c2ecf20Sopenharmony_ci		return false;
988c2ecf20Sopenharmony_ci	cmdline.option = EFI_CMDLINE_AUTO;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	*next = option;
1018c2ecf20Sopenharmony_ci	return true;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic bool parse_list(char *option, char **next)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	if (!strstarts(option, "list"))
1078c2ecf20Sopenharmony_ci		return false;
1088c2ecf20Sopenharmony_ci	option += strlen("list");
1098c2ecf20Sopenharmony_ci	if (*option && *option++ != ',')
1108c2ecf20Sopenharmony_ci		return false;
1118c2ecf20Sopenharmony_ci	cmdline.option = EFI_CMDLINE_LIST;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	*next = option;
1148c2ecf20Sopenharmony_ci	return true;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_civoid efi_parse_option_graphics(char *option)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	while (*option) {
1208c2ecf20Sopenharmony_ci		if (parse_modenum(option, &option))
1218c2ecf20Sopenharmony_ci			continue;
1228c2ecf20Sopenharmony_ci		if (parse_res(option, &option))
1238c2ecf20Sopenharmony_ci			continue;
1248c2ecf20Sopenharmony_ci		if (parse_auto(option, &option))
1258c2ecf20Sopenharmony_ci			continue;
1268c2ecf20Sopenharmony_ci		if (parse_list(option, &option))
1278c2ecf20Sopenharmony_ci			continue;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		while (*option && *option++ != ',')
1308c2ecf20Sopenharmony_ci			;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	efi_status_t status;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
1398c2ecf20Sopenharmony_ci	efi_graphics_output_mode_info_t *info;
1408c2ecf20Sopenharmony_ci	unsigned long info_size;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	u32 max_mode, cur_mode;
1438c2ecf20Sopenharmony_ci	int pf;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	cur_mode = efi_table_attr(mode, mode);
1488c2ecf20Sopenharmony_ci	if (cmdline.mode == cur_mode)
1498c2ecf20Sopenharmony_ci		return cur_mode;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	max_mode = efi_table_attr(mode, max_mode);
1528c2ecf20Sopenharmony_ci	if (cmdline.mode >= max_mode) {
1538c2ecf20Sopenharmony_ci		efi_err("Requested mode is invalid\n");
1548c2ecf20Sopenharmony_ci		return cur_mode;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	status = efi_call_proto(gop, query_mode, cmdline.mode,
1588c2ecf20Sopenharmony_ci				&info_size, &info);
1598c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS) {
1608c2ecf20Sopenharmony_ci		efi_err("Couldn't get mode information\n");
1618c2ecf20Sopenharmony_ci		return cur_mode;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	pf = info->pixel_format;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	efi_bs_call(free_pool, info);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
1698c2ecf20Sopenharmony_ci		efi_err("Invalid PixelFormat\n");
1708c2ecf20Sopenharmony_ci		return cur_mode;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return cmdline.mode;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if (pixel_format == PIXEL_BIT_MASK) {
1798c2ecf20Sopenharmony_ci		u32 mask = pixel_info.red_mask | pixel_info.green_mask |
1808c2ecf20Sopenharmony_ci			   pixel_info.blue_mask | pixel_info.reserved_mask;
1818c2ecf20Sopenharmony_ci		if (!mask)
1828c2ecf20Sopenharmony_ci			return 0;
1838c2ecf20Sopenharmony_ci		return __fls(mask) - __ffs(mask) + 1;
1848c2ecf20Sopenharmony_ci	} else
1858c2ecf20Sopenharmony_ci		return 32;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	efi_status_t status;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
1938c2ecf20Sopenharmony_ci	efi_graphics_output_mode_info_t *info;
1948c2ecf20Sopenharmony_ci	unsigned long info_size;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	u32 max_mode, cur_mode;
1978c2ecf20Sopenharmony_ci	int pf;
1988c2ecf20Sopenharmony_ci	efi_pixel_bitmask_t pi;
1998c2ecf20Sopenharmony_ci	u32 m, w, h;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	cur_mode = efi_table_attr(mode, mode);
2048c2ecf20Sopenharmony_ci	info = efi_table_attr(mode, info);
2058c2ecf20Sopenharmony_ci	pf = info->pixel_format;
2068c2ecf20Sopenharmony_ci	pi = info->pixel_information;
2078c2ecf20Sopenharmony_ci	w  = info->horizontal_resolution;
2088c2ecf20Sopenharmony_ci	h  = info->vertical_resolution;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (w == cmdline.res.width && h == cmdline.res.height &&
2118c2ecf20Sopenharmony_ci	    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2128c2ecf20Sopenharmony_ci	    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
2138c2ecf20Sopenharmony_ci		return cur_mode;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	max_mode = efi_table_attr(mode, max_mode);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	for (m = 0; m < max_mode; m++) {
2188c2ecf20Sopenharmony_ci		if (m == cur_mode)
2198c2ecf20Sopenharmony_ci			continue;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		status = efi_call_proto(gop, query_mode, m,
2228c2ecf20Sopenharmony_ci					&info_size, &info);
2238c2ecf20Sopenharmony_ci		if (status != EFI_SUCCESS)
2248c2ecf20Sopenharmony_ci			continue;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		pf = info->pixel_format;
2278c2ecf20Sopenharmony_ci		pi = info->pixel_information;
2288c2ecf20Sopenharmony_ci		w  = info->horizontal_resolution;
2298c2ecf20Sopenharmony_ci		h  = info->vertical_resolution;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		efi_bs_call(free_pool, info);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
2348c2ecf20Sopenharmony_ci			continue;
2358c2ecf20Sopenharmony_ci		if (w == cmdline.res.width && h == cmdline.res.height &&
2368c2ecf20Sopenharmony_ci		    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2378c2ecf20Sopenharmony_ci		    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
2388c2ecf20Sopenharmony_ci			return m;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	efi_err("Couldn't find requested mode\n");
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return cur_mode;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	efi_status_t status;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
2518c2ecf20Sopenharmony_ci	efi_graphics_output_mode_info_t *info;
2528c2ecf20Sopenharmony_ci	unsigned long info_size;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	u32 max_mode, cur_mode, best_mode, area;
2558c2ecf20Sopenharmony_ci	u8 depth;
2568c2ecf20Sopenharmony_ci	int pf;
2578c2ecf20Sopenharmony_ci	efi_pixel_bitmask_t pi;
2588c2ecf20Sopenharmony_ci	u32 m, w, h, a;
2598c2ecf20Sopenharmony_ci	u8 d;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	cur_mode = efi_table_attr(mode, mode);
2648c2ecf20Sopenharmony_ci	max_mode = efi_table_attr(mode, max_mode);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	info = efi_table_attr(mode, info);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	pf = info->pixel_format;
2698c2ecf20Sopenharmony_ci	pi = info->pixel_information;
2708c2ecf20Sopenharmony_ci	w  = info->horizontal_resolution;
2718c2ecf20Sopenharmony_ci	h  = info->vertical_resolution;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	best_mode = cur_mode;
2748c2ecf20Sopenharmony_ci	area = w * h;
2758c2ecf20Sopenharmony_ci	depth = pixel_bpp(pf, pi);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	for (m = 0; m < max_mode; m++) {
2788c2ecf20Sopenharmony_ci		if (m == cur_mode)
2798c2ecf20Sopenharmony_ci			continue;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		status = efi_call_proto(gop, query_mode, m,
2828c2ecf20Sopenharmony_ci					&info_size, &info);
2838c2ecf20Sopenharmony_ci		if (status != EFI_SUCCESS)
2848c2ecf20Sopenharmony_ci			continue;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		pf = info->pixel_format;
2878c2ecf20Sopenharmony_ci		pi = info->pixel_information;
2888c2ecf20Sopenharmony_ci		w  = info->horizontal_resolution;
2898c2ecf20Sopenharmony_ci		h  = info->vertical_resolution;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		efi_bs_call(free_pool, info);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
2948c2ecf20Sopenharmony_ci			continue;
2958c2ecf20Sopenharmony_ci		a = w * h;
2968c2ecf20Sopenharmony_ci		if (a < area)
2978c2ecf20Sopenharmony_ci			continue;
2988c2ecf20Sopenharmony_ci		d = pixel_bpp(pf, pi);
2998c2ecf20Sopenharmony_ci		if (a > area || d > depth) {
3008c2ecf20Sopenharmony_ci			best_mode = m;
3018c2ecf20Sopenharmony_ci			area = a;
3028c2ecf20Sopenharmony_ci			depth = d;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return best_mode;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	efi_status_t status;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
3148c2ecf20Sopenharmony_ci	efi_graphics_output_mode_info_t *info;
3158c2ecf20Sopenharmony_ci	unsigned long info_size;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	u32 max_mode, cur_mode;
3188c2ecf20Sopenharmony_ci	int pf;
3198c2ecf20Sopenharmony_ci	efi_pixel_bitmask_t pi;
3208c2ecf20Sopenharmony_ci	u32 m, w, h;
3218c2ecf20Sopenharmony_ci	u8 d;
3228c2ecf20Sopenharmony_ci	const char *dstr;
3238c2ecf20Sopenharmony_ci	bool valid;
3248c2ecf20Sopenharmony_ci	efi_input_key_t key;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	cur_mode = efi_table_attr(mode, mode);
3298c2ecf20Sopenharmony_ci	max_mode = efi_table_attr(mode, max_mode);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
3328c2ecf20Sopenharmony_ci	efi_puts("  * = current mode\n"
3338c2ecf20Sopenharmony_ci		 "  - = unusable mode\n");
3348c2ecf20Sopenharmony_ci	for (m = 0; m < max_mode; m++) {
3358c2ecf20Sopenharmony_ci		status = efi_call_proto(gop, query_mode, m,
3368c2ecf20Sopenharmony_ci					&info_size, &info);
3378c2ecf20Sopenharmony_ci		if (status != EFI_SUCCESS)
3388c2ecf20Sopenharmony_ci			continue;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		pf = info->pixel_format;
3418c2ecf20Sopenharmony_ci		pi = info->pixel_information;
3428c2ecf20Sopenharmony_ci		w  = info->horizontal_resolution;
3438c2ecf20Sopenharmony_ci		h  = info->vertical_resolution;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		efi_bs_call(free_pool, info);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
3488c2ecf20Sopenharmony_ci		d = 0;
3498c2ecf20Sopenharmony_ci		switch (pf) {
3508c2ecf20Sopenharmony_ci		case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
3518c2ecf20Sopenharmony_ci			dstr = "rgb";
3528c2ecf20Sopenharmony_ci			break;
3538c2ecf20Sopenharmony_ci		case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
3548c2ecf20Sopenharmony_ci			dstr = "bgr";
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		case PIXEL_BIT_MASK:
3578c2ecf20Sopenharmony_ci			dstr = "";
3588c2ecf20Sopenharmony_ci			d = pixel_bpp(pf, pi);
3598c2ecf20Sopenharmony_ci			break;
3608c2ecf20Sopenharmony_ci		case PIXEL_BLT_ONLY:
3618c2ecf20Sopenharmony_ci			dstr = "blt";
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci		default:
3648c2ecf20Sopenharmony_ci			dstr = "xxx";
3658c2ecf20Sopenharmony_ci			break;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
3698c2ecf20Sopenharmony_ci			   m,
3708c2ecf20Sopenharmony_ci			   m == cur_mode ? '*' : ' ',
3718c2ecf20Sopenharmony_ci			   !valid ? '-' : ' ',
3728c2ecf20Sopenharmony_ci			   w, h, dstr, d);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
3768c2ecf20Sopenharmony_ci	status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
3778c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
3788c2ecf20Sopenharmony_ci		efi_err("Unable to read key, continuing in 10 seconds\n");
3798c2ecf20Sopenharmony_ci		efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return cur_mode;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void set_mode(efi_graphics_output_protocol_t *gop)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
3888c2ecf20Sopenharmony_ci	u32 cur_mode, new_mode;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (cmdline.option) {
3918c2ecf20Sopenharmony_ci	case EFI_CMDLINE_MODE_NUM:
3928c2ecf20Sopenharmony_ci		new_mode = choose_mode_modenum(gop);
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci	case EFI_CMDLINE_RES:
3958c2ecf20Sopenharmony_ci		new_mode = choose_mode_res(gop);
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	case EFI_CMDLINE_AUTO:
3988c2ecf20Sopenharmony_ci		new_mode = choose_mode_auto(gop);
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci	case EFI_CMDLINE_LIST:
4018c2ecf20Sopenharmony_ci		new_mode = choose_mode_list(gop);
4028c2ecf20Sopenharmony_ci		break;
4038c2ecf20Sopenharmony_ci	default:
4048c2ecf20Sopenharmony_ci		return;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
4088c2ecf20Sopenharmony_ci	cur_mode = efi_table_attr(mode, mode);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (new_mode == cur_mode)
4118c2ecf20Sopenharmony_ci		return;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
4148c2ecf20Sopenharmony_ci		efi_err("Failed to set requested mode\n");
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void find_bits(u32 mask, u8 *pos, u8 *size)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	if (!mask) {
4208c2ecf20Sopenharmony_ci		*pos = *size = 0;
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* UEFI spec guarantees that the set bits are contiguous */
4258c2ecf20Sopenharmony_ci	*pos  = __ffs(mask);
4268c2ecf20Sopenharmony_ci	*size = __fls(mask) - *pos + 1;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void
4308c2ecf20Sopenharmony_cisetup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
4318c2ecf20Sopenharmony_ci		 efi_pixel_bitmask_t pixel_info, int pixel_format)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	if (pixel_format == PIXEL_BIT_MASK) {
4348c2ecf20Sopenharmony_ci		find_bits(pixel_info.red_mask,
4358c2ecf20Sopenharmony_ci			  &si->red_pos, &si->red_size);
4368c2ecf20Sopenharmony_ci		find_bits(pixel_info.green_mask,
4378c2ecf20Sopenharmony_ci			  &si->green_pos, &si->green_size);
4388c2ecf20Sopenharmony_ci		find_bits(pixel_info.blue_mask,
4398c2ecf20Sopenharmony_ci			  &si->blue_pos, &si->blue_size);
4408c2ecf20Sopenharmony_ci		find_bits(pixel_info.reserved_mask,
4418c2ecf20Sopenharmony_ci			  &si->rsvd_pos, &si->rsvd_size);
4428c2ecf20Sopenharmony_ci		si->lfb_depth = si->red_size + si->green_size +
4438c2ecf20Sopenharmony_ci			si->blue_size + si->rsvd_size;
4448c2ecf20Sopenharmony_ci		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
4458c2ecf20Sopenharmony_ci	} else {
4468c2ecf20Sopenharmony_ci		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
4478c2ecf20Sopenharmony_ci			si->red_pos   = 0;
4488c2ecf20Sopenharmony_ci			si->blue_pos  = 16;
4498c2ecf20Sopenharmony_ci		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
4508c2ecf20Sopenharmony_ci			si->blue_pos  = 0;
4518c2ecf20Sopenharmony_ci			si->red_pos   = 16;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		si->green_pos = 8;
4558c2ecf20Sopenharmony_ci		si->rsvd_pos  = 24;
4568c2ecf20Sopenharmony_ci		si->red_size = si->green_size =
4578c2ecf20Sopenharmony_ci			si->blue_size = si->rsvd_size = 8;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		si->lfb_depth = 32;
4608c2ecf20Sopenharmony_ci		si->lfb_linelength = pixels_per_scan_line * 4;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic efi_graphics_output_protocol_t *
4658c2ecf20Sopenharmony_cifind_gop(efi_guid_t *proto, unsigned long size, void **handles)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_t *first_gop;
4688c2ecf20Sopenharmony_ci	efi_handle_t h;
4698c2ecf20Sopenharmony_ci	int i;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	first_gop = NULL;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	for_each_efi_handle(h, handles, size, i) {
4748c2ecf20Sopenharmony_ci		efi_status_t status;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		efi_graphics_output_protocol_t *gop;
4778c2ecf20Sopenharmony_ci		efi_graphics_output_protocol_mode_t *mode;
4788c2ecf20Sopenharmony_ci		efi_graphics_output_mode_info_t *info;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
4818c2ecf20Sopenharmony_ci		void *dummy = NULL;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
4848c2ecf20Sopenharmony_ci		if (status != EFI_SUCCESS)
4858c2ecf20Sopenharmony_ci			continue;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		mode = efi_table_attr(gop, mode);
4888c2ecf20Sopenharmony_ci		info = efi_table_attr(mode, info);
4898c2ecf20Sopenharmony_ci		if (info->pixel_format == PIXEL_BLT_ONLY ||
4908c2ecf20Sopenharmony_ci		    info->pixel_format >= PIXEL_FORMAT_MAX)
4918c2ecf20Sopenharmony_ci			continue;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		/*
4948c2ecf20Sopenharmony_ci		 * Systems that use the UEFI Console Splitter may
4958c2ecf20Sopenharmony_ci		 * provide multiple GOP devices, not all of which are
4968c2ecf20Sopenharmony_ci		 * backed by real hardware. The workaround is to search
4978c2ecf20Sopenharmony_ci		 * for a GOP implementing the ConOut protocol, and if
4988c2ecf20Sopenharmony_ci		 * one isn't found, to just fall back to the first GOP.
4998c2ecf20Sopenharmony_ci		 *
5008c2ecf20Sopenharmony_ci		 * Once we've found a GOP supporting ConOut,
5018c2ecf20Sopenharmony_ci		 * don't bother looking any further.
5028c2ecf20Sopenharmony_ci		 */
5038c2ecf20Sopenharmony_ci		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
5048c2ecf20Sopenharmony_ci		if (status == EFI_SUCCESS)
5058c2ecf20Sopenharmony_ci			return gop;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		if (!first_gop)
5088c2ecf20Sopenharmony_ci			first_gop = gop;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return first_gop;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
5158c2ecf20Sopenharmony_ci			      unsigned long size, void **handles)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_t *gop;
5188c2ecf20Sopenharmony_ci	efi_graphics_output_protocol_mode_t *mode;
5198c2ecf20Sopenharmony_ci	efi_graphics_output_mode_info_t *info;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	gop = find_gop(proto, size, handles);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Did we find any GOPs? */
5248c2ecf20Sopenharmony_ci	if (!gop)
5258c2ecf20Sopenharmony_ci		return EFI_NOT_FOUND;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Change mode if requested */
5288c2ecf20Sopenharmony_ci	set_mode(gop);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/* EFI framebuffer */
5318c2ecf20Sopenharmony_ci	mode = efi_table_attr(gop, mode);
5328c2ecf20Sopenharmony_ci	info = efi_table_attr(mode, info);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	si->orig_video_isVGA = VIDEO_TYPE_EFI;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	si->lfb_width  = info->horizontal_resolution;
5378c2ecf20Sopenharmony_ci	si->lfb_height = info->vertical_resolution;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
5408c2ecf20Sopenharmony_ci			  &si->lfb_base, &si->ext_lfb_base);
5418c2ecf20Sopenharmony_ci	if (si->ext_lfb_base)
5428c2ecf20Sopenharmony_ci		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	si->pages = 1;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	setup_pixel_info(si, info->pixels_per_scan_line,
5478c2ecf20Sopenharmony_ci			     info->pixel_information, info->pixel_format);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	si->lfb_size = si->lfb_linelength * si->lfb_height;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return EFI_SUCCESS;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/*
5578c2ecf20Sopenharmony_ci * See if we have Graphics Output Protocol
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_ciefi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
5608c2ecf20Sopenharmony_ci			   unsigned long size)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	efi_status_t status;
5638c2ecf20Sopenharmony_ci	void **gop_handle = NULL;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
5668c2ecf20Sopenharmony_ci			     (void **)&gop_handle);
5678c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
5688c2ecf20Sopenharmony_ci		return status;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
5718c2ecf20Sopenharmony_ci			     &size, gop_handle);
5728c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
5738c2ecf20Sopenharmony_ci		goto free_handle;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	status = setup_gop(si, proto, size, gop_handle);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cifree_handle:
5788c2ecf20Sopenharmony_ci	efi_bs_call(free_pool, gop_handle);
5798c2ecf20Sopenharmony_ci	return status;
5808c2ecf20Sopenharmony_ci}
581