xref: /openbmc/linux/drivers/firmware/efi/libstub/gop.c (revision 14c574f3)
14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2fc372064SArd Biesheuvel /* -----------------------------------------------------------------------
3fc372064SArd Biesheuvel  *
4fc372064SArd Biesheuvel  *   Copyright 2011 Intel Corporation; author Matt Fleming
5fc372064SArd Biesheuvel  *
6fc372064SArd Biesheuvel  * ----------------------------------------------------------------------- */
7fc372064SArd Biesheuvel 
89867fc9dSArvind Sankar #include <linux/bitops.h>
9d9ff0323SArvind Sankar #include <linux/ctype.h>
10fc372064SArd Biesheuvel #include <linux/efi.h>
11fc372064SArd Biesheuvel #include <linux/screen_info.h>
12fffb6804SArvind Sankar #include <linux/string.h>
13fc372064SArd Biesheuvel #include <asm/efi.h>
14fc372064SArd Biesheuvel #include <asm/setup.h>
15fc372064SArd Biesheuvel 
162fcdad2aSArd Biesheuvel #include "efistub.h"
172fcdad2aSArd Biesheuvel 
18fffb6804SArvind Sankar enum efi_cmdline_option {
19fffb6804SArvind Sankar 	EFI_CMDLINE_NONE,
20fffb6804SArvind Sankar 	EFI_CMDLINE_MODE_NUM,
2145d97a74SArvind Sankar 	EFI_CMDLINE_RES,
2214c574f3SArvind Sankar 	EFI_CMDLINE_AUTO,
2314c574f3SArvind Sankar 	EFI_CMDLINE_LIST
24fffb6804SArvind Sankar };
25fffb6804SArvind Sankar 
26fffb6804SArvind Sankar static struct {
27fffb6804SArvind Sankar 	enum efi_cmdline_option option;
28d9ff0323SArvind Sankar 	union {
29fffb6804SArvind Sankar 		u32 mode;
30d9ff0323SArvind Sankar 		struct {
31d9ff0323SArvind Sankar 			u32 width, height;
329a1663bcSArvind Sankar 			int format;
339a1663bcSArvind Sankar 			u8 depth;
34d9ff0323SArvind Sankar 		} res;
35d9ff0323SArvind Sankar 	};
3654439370SArvind Sankar } cmdline = { .option = EFI_CMDLINE_NONE };
37fffb6804SArvind Sankar 
parse_modenum(char * option,char ** next)38fffb6804SArvind Sankar static bool parse_modenum(char *option, char **next)
39fffb6804SArvind Sankar {
40fffb6804SArvind Sankar 	u32 m;
41fffb6804SArvind Sankar 
42fffb6804SArvind Sankar 	if (!strstarts(option, "mode="))
43fffb6804SArvind Sankar 		return false;
44fffb6804SArvind Sankar 	option += strlen("mode=");
45fffb6804SArvind Sankar 	m = simple_strtoull(option, &option, 0);
46fffb6804SArvind Sankar 	if (*option && *option++ != ',')
47fffb6804SArvind Sankar 		return false;
48fffb6804SArvind Sankar 	cmdline.option = EFI_CMDLINE_MODE_NUM;
49fffb6804SArvind Sankar 	cmdline.mode   = m;
50fffb6804SArvind Sankar 
51fffb6804SArvind Sankar 	*next = option;
52fffb6804SArvind Sankar 	return true;
53fffb6804SArvind Sankar }
54fffb6804SArvind Sankar 
parse_res(char * option,char ** next)55d9ff0323SArvind Sankar static bool parse_res(char *option, char **next)
56d9ff0323SArvind Sankar {
579a1663bcSArvind Sankar 	u32 w, h, d = 0;
589a1663bcSArvind Sankar 	int pf = -1;
59d9ff0323SArvind Sankar 
60d9ff0323SArvind Sankar 	if (!isdigit(*option))
61d9ff0323SArvind Sankar 		return false;
62d9ff0323SArvind Sankar 	w = simple_strtoull(option, &option, 10);
63d9ff0323SArvind Sankar 	if (*option++ != 'x' || !isdigit(*option))
64d9ff0323SArvind Sankar 		return false;
65d9ff0323SArvind Sankar 	h = simple_strtoull(option, &option, 10);
669a1663bcSArvind Sankar 	if (*option == '-') {
679a1663bcSArvind Sankar 		option++;
689a1663bcSArvind Sankar 		if (strstarts(option, "rgb")) {
699a1663bcSArvind Sankar 			option += strlen("rgb");
709a1663bcSArvind Sankar 			pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
719a1663bcSArvind Sankar 		} else if (strstarts(option, "bgr")) {
729a1663bcSArvind Sankar 			option += strlen("bgr");
739a1663bcSArvind Sankar 			pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
749a1663bcSArvind Sankar 		} else if (isdigit(*option))
759a1663bcSArvind Sankar 			d = simple_strtoull(option, &option, 10);
769a1663bcSArvind Sankar 		else
779a1663bcSArvind Sankar 			return false;
789a1663bcSArvind Sankar 	}
79d9ff0323SArvind Sankar 	if (*option && *option++ != ',')
80d9ff0323SArvind Sankar 		return false;
81d9ff0323SArvind Sankar 	cmdline.option     = EFI_CMDLINE_RES;
82d9ff0323SArvind Sankar 	cmdline.res.width  = w;
83d9ff0323SArvind Sankar 	cmdline.res.height = h;
849a1663bcSArvind Sankar 	cmdline.res.format = pf;
859a1663bcSArvind Sankar 	cmdline.res.depth  = d;
86d9ff0323SArvind Sankar 
87d9ff0323SArvind Sankar 	*next = option;
88d9ff0323SArvind Sankar 	return true;
89d9ff0323SArvind Sankar }
90d9ff0323SArvind Sankar 
parse_auto(char * option,char ** next)9145d97a74SArvind Sankar static bool parse_auto(char *option, char **next)
9245d97a74SArvind Sankar {
9345d97a74SArvind Sankar 	if (!strstarts(option, "auto"))
9445d97a74SArvind Sankar 		return false;
9545d97a74SArvind Sankar 	option += strlen("auto");
9645d97a74SArvind Sankar 	if (*option && *option++ != ',')
9745d97a74SArvind Sankar 		return false;
9845d97a74SArvind Sankar 	cmdline.option = EFI_CMDLINE_AUTO;
9945d97a74SArvind Sankar 
10045d97a74SArvind Sankar 	*next = option;
10145d97a74SArvind Sankar 	return true;
10245d97a74SArvind Sankar }
10345d97a74SArvind Sankar 
parse_list(char * option,char ** next)10414c574f3SArvind Sankar static bool parse_list(char *option, char **next)
10514c574f3SArvind Sankar {
10614c574f3SArvind Sankar 	if (!strstarts(option, "list"))
10714c574f3SArvind Sankar 		return false;
10814c574f3SArvind Sankar 	option += strlen("list");
10914c574f3SArvind Sankar 	if (*option && *option++ != ',')
11014c574f3SArvind Sankar 		return false;
11114c574f3SArvind Sankar 	cmdline.option = EFI_CMDLINE_LIST;
11214c574f3SArvind Sankar 
11314c574f3SArvind Sankar 	*next = option;
11414c574f3SArvind Sankar 	return true;
11514c574f3SArvind Sankar }
11614c574f3SArvind Sankar 
efi_parse_option_graphics(char * option)117fffb6804SArvind Sankar void efi_parse_option_graphics(char *option)
118fffb6804SArvind Sankar {
119fffb6804SArvind Sankar 	while (*option) {
120fffb6804SArvind Sankar 		if (parse_modenum(option, &option))
121fffb6804SArvind Sankar 			continue;
122d9ff0323SArvind Sankar 		if (parse_res(option, &option))
123d9ff0323SArvind Sankar 			continue;
12445d97a74SArvind Sankar 		if (parse_auto(option, &option))
12545d97a74SArvind Sankar 			continue;
12614c574f3SArvind Sankar 		if (parse_list(option, &option))
12714c574f3SArvind Sankar 			continue;
128fffb6804SArvind Sankar 
129fffb6804SArvind Sankar 		while (*option && *option++ != ',')
130fffb6804SArvind Sankar 			;
131fffb6804SArvind Sankar 	}
132fffb6804SArvind Sankar }
133fffb6804SArvind Sankar 
choose_mode_modenum(efi_graphics_output_protocol_t * gop)134fffb6804SArvind Sankar static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
135fffb6804SArvind Sankar {
136fffb6804SArvind Sankar 	efi_status_t status;
137fffb6804SArvind Sankar 
138fffb6804SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
139fffb6804SArvind Sankar 	efi_graphics_output_mode_info_t *info;
140fffb6804SArvind Sankar 	unsigned long info_size;
141fffb6804SArvind Sankar 
142fffb6804SArvind Sankar 	u32 max_mode, cur_mode;
143fffb6804SArvind Sankar 	int pf;
144fffb6804SArvind Sankar 
145fffb6804SArvind Sankar 	mode = efi_table_attr(gop, mode);
146fffb6804SArvind Sankar 
147fffb6804SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
148fffb6804SArvind Sankar 	if (cmdline.mode == cur_mode)
149fffb6804SArvind Sankar 		return cur_mode;
150fffb6804SArvind Sankar 
151fffb6804SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
152fffb6804SArvind Sankar 	if (cmdline.mode >= max_mode) {
15361eac6d9SArvind Sankar 		efi_err("Requested mode is invalid\n");
154fffb6804SArvind Sankar 		return cur_mode;
155fffb6804SArvind Sankar 	}
156fffb6804SArvind Sankar 
157fffb6804SArvind Sankar 	status = efi_call_proto(gop, query_mode, cmdline.mode,
158fffb6804SArvind Sankar 				&info_size, &info);
159fffb6804SArvind Sankar 	if (status != EFI_SUCCESS) {
16061eac6d9SArvind Sankar 		efi_err("Couldn't get mode information\n");
161fffb6804SArvind Sankar 		return cur_mode;
162fffb6804SArvind Sankar 	}
163fffb6804SArvind Sankar 
164fffb6804SArvind Sankar 	pf = info->pixel_format;
165fffb6804SArvind Sankar 
166fffb6804SArvind Sankar 	efi_bs_call(free_pool, info);
167fffb6804SArvind Sankar 
168fffb6804SArvind Sankar 	if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
16961eac6d9SArvind Sankar 		efi_err("Invalid PixelFormat\n");
170fffb6804SArvind Sankar 		return cur_mode;
171fffb6804SArvind Sankar 	}
172fffb6804SArvind Sankar 
173fffb6804SArvind Sankar 	return cmdline.mode;
174fffb6804SArvind Sankar }
175fffb6804SArvind Sankar 
pixel_bpp(int pixel_format,efi_pixel_bitmask_t pixel_info)1769a1663bcSArvind Sankar static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
1779a1663bcSArvind Sankar {
1789a1663bcSArvind Sankar 	if (pixel_format == PIXEL_BIT_MASK) {
1799a1663bcSArvind Sankar 		u32 mask = pixel_info.red_mask | pixel_info.green_mask |
1809a1663bcSArvind Sankar 			   pixel_info.blue_mask | pixel_info.reserved_mask;
1819a1663bcSArvind Sankar 		if (!mask)
1829a1663bcSArvind Sankar 			return 0;
1839a1663bcSArvind Sankar 		return __fls(mask) - __ffs(mask) + 1;
1849a1663bcSArvind Sankar 	} else
1859a1663bcSArvind Sankar 		return 32;
1869a1663bcSArvind Sankar }
1879a1663bcSArvind Sankar 
choose_mode_res(efi_graphics_output_protocol_t * gop)188d9ff0323SArvind Sankar static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
189d9ff0323SArvind Sankar {
190d9ff0323SArvind Sankar 	efi_status_t status;
191d9ff0323SArvind Sankar 
192d9ff0323SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
193d9ff0323SArvind Sankar 	efi_graphics_output_mode_info_t *info;
194d9ff0323SArvind Sankar 	unsigned long info_size;
195d9ff0323SArvind Sankar 
196d9ff0323SArvind Sankar 	u32 max_mode, cur_mode;
197d9ff0323SArvind Sankar 	int pf;
1989a1663bcSArvind Sankar 	efi_pixel_bitmask_t pi;
199d9ff0323SArvind Sankar 	u32 m, w, h;
200d9ff0323SArvind Sankar 
201d9ff0323SArvind Sankar 	mode = efi_table_attr(gop, mode);
202d9ff0323SArvind Sankar 
203d9ff0323SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
204d9ff0323SArvind Sankar 	info = efi_table_attr(mode, info);
2059a1663bcSArvind Sankar 	pf = info->pixel_format;
2069a1663bcSArvind Sankar 	pi = info->pixel_information;
207d9ff0323SArvind Sankar 	w  = info->horizontal_resolution;
208d9ff0323SArvind Sankar 	h  = info->vertical_resolution;
209d9ff0323SArvind Sankar 
2109a1663bcSArvind Sankar 	if (w == cmdline.res.width && h == cmdline.res.height &&
2119a1663bcSArvind Sankar 	    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2129a1663bcSArvind Sankar 	    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
213d9ff0323SArvind Sankar 		return cur_mode;
214d9ff0323SArvind Sankar 
215d9ff0323SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
216d9ff0323SArvind Sankar 
217d9ff0323SArvind Sankar 	for (m = 0; m < max_mode; m++) {
218d9ff0323SArvind Sankar 		if (m == cur_mode)
219d9ff0323SArvind Sankar 			continue;
220d9ff0323SArvind Sankar 
221d9ff0323SArvind Sankar 		status = efi_call_proto(gop, query_mode, m,
222d9ff0323SArvind Sankar 					&info_size, &info);
223d9ff0323SArvind Sankar 		if (status != EFI_SUCCESS)
224d9ff0323SArvind Sankar 			continue;
225d9ff0323SArvind Sankar 
226d9ff0323SArvind Sankar 		pf = info->pixel_format;
2279a1663bcSArvind Sankar 		pi = info->pixel_information;
228d9ff0323SArvind Sankar 		w  = info->horizontal_resolution;
229d9ff0323SArvind Sankar 		h  = info->vertical_resolution;
230d9ff0323SArvind Sankar 
231d9ff0323SArvind Sankar 		efi_bs_call(free_pool, info);
232d9ff0323SArvind Sankar 
233d9ff0323SArvind Sankar 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
234d9ff0323SArvind Sankar 			continue;
2359a1663bcSArvind Sankar 		if (w == cmdline.res.width && h == cmdline.res.height &&
2369a1663bcSArvind Sankar 		    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2379a1663bcSArvind Sankar 		    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
238d9ff0323SArvind Sankar 			return m;
239d9ff0323SArvind Sankar 	}
240d9ff0323SArvind Sankar 
24161eac6d9SArvind Sankar 	efi_err("Couldn't find requested mode\n");
242d9ff0323SArvind Sankar 
243d9ff0323SArvind Sankar 	return cur_mode;
244d9ff0323SArvind Sankar }
245d9ff0323SArvind Sankar 
choose_mode_auto(efi_graphics_output_protocol_t * gop)24645d97a74SArvind Sankar static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
24745d97a74SArvind Sankar {
24845d97a74SArvind Sankar 	efi_status_t status;
24945d97a74SArvind Sankar 
25045d97a74SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
25145d97a74SArvind Sankar 	efi_graphics_output_mode_info_t *info;
25245d97a74SArvind Sankar 	unsigned long info_size;
25345d97a74SArvind Sankar 
25445d97a74SArvind Sankar 	u32 max_mode, cur_mode, best_mode, area;
25545d97a74SArvind Sankar 	u8 depth;
25645d97a74SArvind Sankar 	int pf;
25745d97a74SArvind Sankar 	efi_pixel_bitmask_t pi;
25845d97a74SArvind Sankar 	u32 m, w, h, a;
25945d97a74SArvind Sankar 	u8 d;
26045d97a74SArvind Sankar 
26145d97a74SArvind Sankar 	mode = efi_table_attr(gop, mode);
26245d97a74SArvind Sankar 
26345d97a74SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
26445d97a74SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
26545d97a74SArvind Sankar 
26645d97a74SArvind Sankar 	info = efi_table_attr(mode, info);
26745d97a74SArvind Sankar 
26845d97a74SArvind Sankar 	pf = info->pixel_format;
26945d97a74SArvind Sankar 	pi = info->pixel_information;
27045d97a74SArvind Sankar 	w  = info->horizontal_resolution;
27145d97a74SArvind Sankar 	h  = info->vertical_resolution;
27245d97a74SArvind Sankar 
27345d97a74SArvind Sankar 	best_mode = cur_mode;
27445d97a74SArvind Sankar 	area = w * h;
27545d97a74SArvind Sankar 	depth = pixel_bpp(pf, pi);
27645d97a74SArvind Sankar 
27745d97a74SArvind Sankar 	for (m = 0; m < max_mode; m++) {
27845d97a74SArvind Sankar 		if (m == cur_mode)
27945d97a74SArvind Sankar 			continue;
28045d97a74SArvind Sankar 
28145d97a74SArvind Sankar 		status = efi_call_proto(gop, query_mode, m,
28245d97a74SArvind Sankar 					&info_size, &info);
28345d97a74SArvind Sankar 		if (status != EFI_SUCCESS)
28445d97a74SArvind Sankar 			continue;
28545d97a74SArvind Sankar 
28645d97a74SArvind Sankar 		pf = info->pixel_format;
28745d97a74SArvind Sankar 		pi = info->pixel_information;
28845d97a74SArvind Sankar 		w  = info->horizontal_resolution;
28945d97a74SArvind Sankar 		h  = info->vertical_resolution;
29045d97a74SArvind Sankar 
29145d97a74SArvind Sankar 		efi_bs_call(free_pool, info);
29245d97a74SArvind Sankar 
29345d97a74SArvind Sankar 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
29445d97a74SArvind Sankar 			continue;
29545d97a74SArvind Sankar 		a = w * h;
29645d97a74SArvind Sankar 		if (a < area)
29745d97a74SArvind Sankar 			continue;
29845d97a74SArvind Sankar 		d = pixel_bpp(pf, pi);
29945d97a74SArvind Sankar 		if (a > area || d > depth) {
30045d97a74SArvind Sankar 			best_mode = m;
30145d97a74SArvind Sankar 			area = a;
30245d97a74SArvind Sankar 			depth = d;
30345d97a74SArvind Sankar 		}
30445d97a74SArvind Sankar 	}
30545d97a74SArvind Sankar 
30645d97a74SArvind Sankar 	return best_mode;
30745d97a74SArvind Sankar }
30845d97a74SArvind Sankar 
choose_mode_list(efi_graphics_output_protocol_t * gop)30914c574f3SArvind Sankar static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
31014c574f3SArvind Sankar {
31114c574f3SArvind Sankar 	efi_status_t status;
31214c574f3SArvind Sankar 
31314c574f3SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
31414c574f3SArvind Sankar 	efi_graphics_output_mode_info_t *info;
31514c574f3SArvind Sankar 	unsigned long info_size;
31614c574f3SArvind Sankar 
31714c574f3SArvind Sankar 	u32 max_mode, cur_mode;
31814c574f3SArvind Sankar 	int pf;
31914c574f3SArvind Sankar 	efi_pixel_bitmask_t pi;
32014c574f3SArvind Sankar 	u32 m, w, h;
32114c574f3SArvind Sankar 	u8 d;
32214c574f3SArvind Sankar 	const char *dstr;
32314c574f3SArvind Sankar 	bool valid;
32414c574f3SArvind Sankar 	efi_input_key_t key;
32514c574f3SArvind Sankar 
32614c574f3SArvind Sankar 	mode = efi_table_attr(gop, mode);
32714c574f3SArvind Sankar 
32814c574f3SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
32914c574f3SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
33014c574f3SArvind Sankar 
33114c574f3SArvind Sankar 	efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
33214c574f3SArvind Sankar 	efi_puts("  * = current mode\n"
33314c574f3SArvind Sankar 		 "  - = unusable mode\n");
33414c574f3SArvind Sankar 	for (m = 0; m < max_mode; m++) {
33514c574f3SArvind Sankar 		status = efi_call_proto(gop, query_mode, m,
33614c574f3SArvind Sankar 					&info_size, &info);
33714c574f3SArvind Sankar 		if (status != EFI_SUCCESS)
33814c574f3SArvind Sankar 			continue;
33914c574f3SArvind Sankar 
34014c574f3SArvind Sankar 		pf = info->pixel_format;
34114c574f3SArvind Sankar 		pi = info->pixel_information;
34214c574f3SArvind Sankar 		w  = info->horizontal_resolution;
34314c574f3SArvind Sankar 		h  = info->vertical_resolution;
34414c574f3SArvind Sankar 
34514c574f3SArvind Sankar 		efi_bs_call(free_pool, info);
34614c574f3SArvind Sankar 
34714c574f3SArvind Sankar 		valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
34814c574f3SArvind Sankar 		d = 0;
34914c574f3SArvind Sankar 		switch (pf) {
35014c574f3SArvind Sankar 		case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
35114c574f3SArvind Sankar 			dstr = "rgb";
35214c574f3SArvind Sankar 			break;
35314c574f3SArvind Sankar 		case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
35414c574f3SArvind Sankar 			dstr = "bgr";
35514c574f3SArvind Sankar 			break;
35614c574f3SArvind Sankar 		case PIXEL_BIT_MASK:
35714c574f3SArvind Sankar 			dstr = "";
35814c574f3SArvind Sankar 			d = pixel_bpp(pf, pi);
35914c574f3SArvind Sankar 			break;
36014c574f3SArvind Sankar 		case PIXEL_BLT_ONLY:
36114c574f3SArvind Sankar 			dstr = "blt";
36214c574f3SArvind Sankar 			break;
36314c574f3SArvind Sankar 		default:
36414c574f3SArvind Sankar 			dstr = "xxx";
36514c574f3SArvind Sankar 			break;
36614c574f3SArvind Sankar 		}
36714c574f3SArvind Sankar 
36814c574f3SArvind Sankar 		efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
36914c574f3SArvind Sankar 			   m,
37014c574f3SArvind Sankar 			   m == cur_mode ? '*' : ' ',
37114c574f3SArvind Sankar 			   !valid ? '-' : ' ',
37214c574f3SArvind Sankar 			   w, h, dstr, d);
37314c574f3SArvind Sankar 	}
37414c574f3SArvind Sankar 
37514c574f3SArvind Sankar 	efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
37614c574f3SArvind Sankar 	status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
37714c574f3SArvind Sankar 	if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
37814c574f3SArvind Sankar 		efi_err("Unable to read key, continuing in 10 seconds\n");
37914c574f3SArvind Sankar 		efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
38014c574f3SArvind Sankar 	}
38114c574f3SArvind Sankar 
38214c574f3SArvind Sankar 	return cur_mode;
38314c574f3SArvind Sankar }
38414c574f3SArvind Sankar 
set_mode(efi_graphics_output_protocol_t * gop)385fffb6804SArvind Sankar static void set_mode(efi_graphics_output_protocol_t *gop)
386fffb6804SArvind Sankar {
387fffb6804SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
388fffb6804SArvind Sankar 	u32 cur_mode, new_mode;
389fffb6804SArvind Sankar 
390fffb6804SArvind Sankar 	switch (cmdline.option) {
391fffb6804SArvind Sankar 	case EFI_CMDLINE_MODE_NUM:
392fffb6804SArvind Sankar 		new_mode = choose_mode_modenum(gop);
393fffb6804SArvind Sankar 		break;
394d9ff0323SArvind Sankar 	case EFI_CMDLINE_RES:
395d9ff0323SArvind Sankar 		new_mode = choose_mode_res(gop);
396d9ff0323SArvind Sankar 		break;
39745d97a74SArvind Sankar 	case EFI_CMDLINE_AUTO:
39845d97a74SArvind Sankar 		new_mode = choose_mode_auto(gop);
39945d97a74SArvind Sankar 		break;
40014c574f3SArvind Sankar 	case EFI_CMDLINE_LIST:
40114c574f3SArvind Sankar 		new_mode = choose_mode_list(gop);
40214c574f3SArvind Sankar 		break;
403fffb6804SArvind Sankar 	default:
404fffb6804SArvind Sankar 		return;
405fffb6804SArvind Sankar 	}
406fffb6804SArvind Sankar 
407fffb6804SArvind Sankar 	mode = efi_table_attr(gop, mode);
408fffb6804SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
409fffb6804SArvind Sankar 
410fffb6804SArvind Sankar 	if (new_mode == cur_mode)
411fffb6804SArvind Sankar 		return;
412fffb6804SArvind Sankar 
413fffb6804SArvind Sankar 	if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
41461eac6d9SArvind Sankar 		efi_err("Failed to set requested mode\n");
415fffb6804SArvind Sankar }
416fffb6804SArvind Sankar 
find_bits(u32 mask,u8 * pos,u8 * size)4179867fc9dSArvind Sankar static void find_bits(u32 mask, u8 *pos, u8 *size)
418fc372064SArd Biesheuvel {
4199867fc9dSArvind Sankar 	if (!mask) {
4209867fc9dSArvind Sankar 		*pos = *size = 0;
4219867fc9dSArvind Sankar 		return;
422fc372064SArd Biesheuvel 	}
423fc372064SArd Biesheuvel 
4249867fc9dSArvind Sankar 	/* UEFI spec guarantees that the set bits are contiguous */
4259867fc9dSArvind Sankar 	*pos  = __ffs(mask);
4269867fc9dSArvind Sankar 	*size = __fls(mask) - *pos + 1;
427fc372064SArd Biesheuvel }
428fc372064SArd Biesheuvel 
429fc372064SArd Biesheuvel static void
setup_pixel_info(struct screen_info * si,u32 pixels_per_scan_line,efi_pixel_bitmask_t pixel_info,int pixel_format)430fc372064SArd Biesheuvel setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
43144c84b4aSArvind Sankar 		 efi_pixel_bitmask_t pixel_info, int pixel_format)
432fc372064SArd Biesheuvel {
433d49fd4bbSArvind Sankar 	if (pixel_format == PIXEL_BIT_MASK) {
434d49fd4bbSArvind Sankar 		find_bits(pixel_info.red_mask,
435d49fd4bbSArvind Sankar 			  &si->red_pos, &si->red_size);
436d49fd4bbSArvind Sankar 		find_bits(pixel_info.green_mask,
437d49fd4bbSArvind Sankar 			  &si->green_pos, &si->green_size);
438d49fd4bbSArvind Sankar 		find_bits(pixel_info.blue_mask,
439d49fd4bbSArvind Sankar 			  &si->blue_pos, &si->blue_size);
440d49fd4bbSArvind Sankar 		find_bits(pixel_info.reserved_mask,
441d49fd4bbSArvind Sankar 			  &si->rsvd_pos, &si->rsvd_size);
442fc372064SArd Biesheuvel 		si->lfb_depth = si->red_size + si->green_size +
443fc372064SArd Biesheuvel 			si->blue_size + si->rsvd_size;
444fc372064SArd Biesheuvel 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
445fc372064SArd Biesheuvel 	} else {
446d49fd4bbSArvind Sankar 		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
447fc372064SArd Biesheuvel 			si->red_pos   = 0;
448d49fd4bbSArvind Sankar 			si->blue_pos  = 16;
449d49fd4bbSArvind Sankar 		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
450fc372064SArd Biesheuvel 			si->blue_pos  = 0;
451d49fd4bbSArvind Sankar 			si->red_pos   = 16;
452d49fd4bbSArvind Sankar 		}
453d49fd4bbSArvind Sankar 
454d49fd4bbSArvind Sankar 		si->green_pos = 8;
455d49fd4bbSArvind Sankar 		si->rsvd_pos  = 24;
456d49fd4bbSArvind Sankar 		si->red_size = si->green_size =
457d49fd4bbSArvind Sankar 			si->blue_size = si->rsvd_size = 8;
458d49fd4bbSArvind Sankar 
459d49fd4bbSArvind Sankar 		si->lfb_depth = 32;
460d49fd4bbSArvind Sankar 		si->lfb_linelength = pixels_per_scan_line * 4;
461fc372064SArd Biesheuvel 	}
462fc372064SArd Biesheuvel }
463fc372064SArd Biesheuvel 
464ecf53091SArvind Sankar static efi_graphics_output_protocol_t *
find_gop(efi_guid_t * proto,unsigned long size,void ** handles)465ecf53091SArvind Sankar find_gop(efi_guid_t *proto, unsigned long size, void **handles)
466fc372064SArd Biesheuvel {
467e484c594SArvind Sankar 	efi_graphics_output_protocol_t *first_gop;
4682732ea0dSArd Biesheuvel 	efi_handle_t h;
469fc372064SArd Biesheuvel 	int i;
470fc372064SArd Biesheuvel 
471fc372064SArd Biesheuvel 	first_gop = NULL;
472fc372064SArd Biesheuvel 
4732732ea0dSArd Biesheuvel 	for_each_efi_handle(h, handles, size, i) {
474e484c594SArvind Sankar 		efi_status_t status;
475e484c594SArvind Sankar 
476e484c594SArvind Sankar 		efi_graphics_output_protocol_t *gop;
477e484c594SArvind Sankar 		efi_graphics_output_protocol_mode_t *mode;
478e484c594SArvind Sankar 		efi_graphics_output_mode_info_t *info;
479e484c594SArvind Sankar 
480fc372064SArd Biesheuvel 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
481fc372064SArd Biesheuvel 		void *dummy = NULL;
482fc372064SArd Biesheuvel 
483966291f6SArd Biesheuvel 		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
484fc372064SArd Biesheuvel 		if (status != EFI_SUCCESS)
485fc372064SArd Biesheuvel 			continue;
486fc372064SArd Biesheuvel 
4878cd20797SArvind Sankar 		mode = efi_table_attr(gop, mode);
4888cd20797SArvind Sankar 		info = efi_table_attr(mode, info);
489d49fd4bbSArvind Sankar 		if (info->pixel_format == PIXEL_BLT_ONLY ||
490d49fd4bbSArvind Sankar 		    info->pixel_format >= PIXEL_FORMAT_MAX)
4918cd20797SArvind Sankar 			continue;
4928cd20797SArvind Sankar 
493fc372064SArd Biesheuvel 		/*
494fc372064SArd Biesheuvel 		 * Systems that use the UEFI Console Splitter may
495fc372064SArd Biesheuvel 		 * provide multiple GOP devices, not all of which are
496fc372064SArd Biesheuvel 		 * backed by real hardware. The workaround is to search
497fc372064SArd Biesheuvel 		 * for a GOP implementing the ConOut protocol, and if
498fc372064SArd Biesheuvel 		 * one isn't found, to just fall back to the first GOP.
4996327e6d0SArvind Sankar 		 *
500fc372064SArd Biesheuvel 		 * Once we've found a GOP supporting ConOut,
501fc372064SArd Biesheuvel 		 * don't bother looking any further.
502fc372064SArd Biesheuvel 		 */
5038e0a22e2SArvind Sankar 		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
5048e0a22e2SArvind Sankar 		if (status == EFI_SUCCESS)
5058e0a22e2SArvind Sankar 			return gop;
5068e0a22e2SArvind Sankar 
5078e0a22e2SArvind Sankar 		if (!first_gop)
5088de8788dSArvind Sankar 			first_gop = gop;
509fc372064SArd Biesheuvel 	}
510fc372064SArd Biesheuvel 
511ecf53091SArvind Sankar 	return first_gop;
512ecf53091SArvind Sankar }
513ecf53091SArvind Sankar 
setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size,void ** handles)514ecf53091SArvind Sankar static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
515ecf53091SArvind Sankar 			      unsigned long size, void **handles)
516ecf53091SArvind Sankar {
517ecf53091SArvind Sankar 	efi_graphics_output_protocol_t *gop;
518ecf53091SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
519e484c594SArvind Sankar 	efi_graphics_output_mode_info_t *info;
520ecf53091SArvind Sankar 
521ecf53091SArvind Sankar 	gop = find_gop(proto, size, handles);
522ecf53091SArvind Sankar 
523fc372064SArd Biesheuvel 	/* Did we find any GOPs? */
524ecf53091SArvind Sankar 	if (!gop)
5256fc3cec3SArvind Sankar 		return EFI_NOT_FOUND;
526fc372064SArd Biesheuvel 
527fffb6804SArvind Sankar 	/* Change mode if requested */
528fffb6804SArvind Sankar 	set_mode(gop);
529fffb6804SArvind Sankar 
530fc372064SArd Biesheuvel 	/* EFI framebuffer */
531ecf53091SArvind Sankar 	mode = efi_table_attr(gop, mode);
5326327e6d0SArvind Sankar 	info = efi_table_attr(mode, info);
5336327e6d0SArvind Sankar 
534fc372064SArd Biesheuvel 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
535fc372064SArd Biesheuvel 
5366327e6d0SArvind Sankar 	si->lfb_width  = info->horizontal_resolution;
5376327e6d0SArvind Sankar 	si->lfb_height = info->vertical_resolution;
538fc372064SArd Biesheuvel 
539eed4e019SArvind Sankar 	efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
540eed4e019SArvind Sankar 			  &si->lfb_base, &si->ext_lfb_base);
5416327e6d0SArvind Sankar 	if (si->ext_lfb_base)
542fc372064SArd Biesheuvel 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
543fc372064SArd Biesheuvel 
544fc372064SArd Biesheuvel 	si->pages = 1;
545fc372064SArd Biesheuvel 
5466327e6d0SArvind Sankar 	setup_pixel_info(si, info->pixels_per_scan_line,
5476327e6d0SArvind Sankar 			     info->pixel_information, info->pixel_format);
548fc372064SArd Biesheuvel 
549fc372064SArd Biesheuvel 	si->lfb_size = si->lfb_linelength * si->lfb_height;
550fc372064SArd Biesheuvel 
551fc372064SArd Biesheuvel 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
5526fc3cec3SArvind Sankar 
553dbd89c30SArvind Sankar 	return EFI_SUCCESS;
554fc372064SArd Biesheuvel }
555fc372064SArd Biesheuvel 
556fc372064SArd Biesheuvel /*
557fc372064SArd Biesheuvel  * See if we have Graphics Output Protocol
558fc372064SArd Biesheuvel  */
efi_setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size)559cd33a5c1SArd Biesheuvel efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
560fc372064SArd Biesheuvel 			   unsigned long size)
561fc372064SArd Biesheuvel {
562fc372064SArd Biesheuvel 	efi_status_t status;
563fc372064SArd Biesheuvel 	void **gop_handle = NULL;
564fc372064SArd Biesheuvel 
565966291f6SArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
566966291f6SArd Biesheuvel 			     (void **)&gop_handle);
567fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
568fc372064SArd Biesheuvel 		return status;
569fc372064SArd Biesheuvel 
570966291f6SArd Biesheuvel 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
571966291f6SArd Biesheuvel 			     &size, gop_handle);
572fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
573fc372064SArd Biesheuvel 		goto free_handle;
574fc372064SArd Biesheuvel 
575cd33a5c1SArd Biesheuvel 	status = setup_gop(si, proto, size, gop_handle);
576fc372064SArd Biesheuvel 
577fc372064SArd Biesheuvel free_handle:
578966291f6SArd Biesheuvel 	efi_bs_call(free_pool, gop_handle);
579fc372064SArd Biesheuvel 	return status;
580fc372064SArd Biesheuvel }
581