xref: /OK3568_Linux_fs/kernel/drivers/firmware/efi/libstub/gop.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* -----------------------------------------------------------------------
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *   Copyright 2011 Intel Corporation; author Matt Fleming
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * ----------------------------------------------------------------------- */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/bitops.h>
9*4882a593Smuzhiyun #include <linux/ctype.h>
10*4882a593Smuzhiyun #include <linux/efi.h>
11*4882a593Smuzhiyun #include <linux/screen_info.h>
12*4882a593Smuzhiyun #include <linux/string.h>
13*4882a593Smuzhiyun #include <asm/efi.h>
14*4882a593Smuzhiyun #include <asm/setup.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "efistub.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun enum efi_cmdline_option {
19*4882a593Smuzhiyun 	EFI_CMDLINE_NONE,
20*4882a593Smuzhiyun 	EFI_CMDLINE_MODE_NUM,
21*4882a593Smuzhiyun 	EFI_CMDLINE_RES,
22*4882a593Smuzhiyun 	EFI_CMDLINE_AUTO,
23*4882a593Smuzhiyun 	EFI_CMDLINE_LIST
24*4882a593Smuzhiyun };
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun static struct {
27*4882a593Smuzhiyun 	enum efi_cmdline_option option;
28*4882a593Smuzhiyun 	union {
29*4882a593Smuzhiyun 		u32 mode;
30*4882a593Smuzhiyun 		struct {
31*4882a593Smuzhiyun 			u32 width, height;
32*4882a593Smuzhiyun 			int format;
33*4882a593Smuzhiyun 			u8 depth;
34*4882a593Smuzhiyun 		} res;
35*4882a593Smuzhiyun 	};
36*4882a593Smuzhiyun } cmdline = { .option = EFI_CMDLINE_NONE };
37*4882a593Smuzhiyun 
parse_modenum(char * option,char ** next)38*4882a593Smuzhiyun static bool parse_modenum(char *option, char **next)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	u32 m;
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	if (!strstarts(option, "mode="))
43*4882a593Smuzhiyun 		return false;
44*4882a593Smuzhiyun 	option += strlen("mode=");
45*4882a593Smuzhiyun 	m = simple_strtoull(option, &option, 0);
46*4882a593Smuzhiyun 	if (*option && *option++ != ',')
47*4882a593Smuzhiyun 		return false;
48*4882a593Smuzhiyun 	cmdline.option = EFI_CMDLINE_MODE_NUM;
49*4882a593Smuzhiyun 	cmdline.mode   = m;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	*next = option;
52*4882a593Smuzhiyun 	return true;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
parse_res(char * option,char ** next)55*4882a593Smuzhiyun static bool parse_res(char *option, char **next)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	u32 w, h, d = 0;
58*4882a593Smuzhiyun 	int pf = -1;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (!isdigit(*option))
61*4882a593Smuzhiyun 		return false;
62*4882a593Smuzhiyun 	w = simple_strtoull(option, &option, 10);
63*4882a593Smuzhiyun 	if (*option++ != 'x' || !isdigit(*option))
64*4882a593Smuzhiyun 		return false;
65*4882a593Smuzhiyun 	h = simple_strtoull(option, &option, 10);
66*4882a593Smuzhiyun 	if (*option == '-') {
67*4882a593Smuzhiyun 		option++;
68*4882a593Smuzhiyun 		if (strstarts(option, "rgb")) {
69*4882a593Smuzhiyun 			option += strlen("rgb");
70*4882a593Smuzhiyun 			pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
71*4882a593Smuzhiyun 		} else if (strstarts(option, "bgr")) {
72*4882a593Smuzhiyun 			option += strlen("bgr");
73*4882a593Smuzhiyun 			pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
74*4882a593Smuzhiyun 		} else if (isdigit(*option))
75*4882a593Smuzhiyun 			d = simple_strtoull(option, &option, 10);
76*4882a593Smuzhiyun 		else
77*4882a593Smuzhiyun 			return false;
78*4882a593Smuzhiyun 	}
79*4882a593Smuzhiyun 	if (*option && *option++ != ',')
80*4882a593Smuzhiyun 		return false;
81*4882a593Smuzhiyun 	cmdline.option     = EFI_CMDLINE_RES;
82*4882a593Smuzhiyun 	cmdline.res.width  = w;
83*4882a593Smuzhiyun 	cmdline.res.height = h;
84*4882a593Smuzhiyun 	cmdline.res.format = pf;
85*4882a593Smuzhiyun 	cmdline.res.depth  = d;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	*next = option;
88*4882a593Smuzhiyun 	return true;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
parse_auto(char * option,char ** next)91*4882a593Smuzhiyun static bool parse_auto(char *option, char **next)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	if (!strstarts(option, "auto"))
94*4882a593Smuzhiyun 		return false;
95*4882a593Smuzhiyun 	option += strlen("auto");
96*4882a593Smuzhiyun 	if (*option && *option++ != ',')
97*4882a593Smuzhiyun 		return false;
98*4882a593Smuzhiyun 	cmdline.option = EFI_CMDLINE_AUTO;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	*next = option;
101*4882a593Smuzhiyun 	return true;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
parse_list(char * option,char ** next)104*4882a593Smuzhiyun static bool parse_list(char *option, char **next)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	if (!strstarts(option, "list"))
107*4882a593Smuzhiyun 		return false;
108*4882a593Smuzhiyun 	option += strlen("list");
109*4882a593Smuzhiyun 	if (*option && *option++ != ',')
110*4882a593Smuzhiyun 		return false;
111*4882a593Smuzhiyun 	cmdline.option = EFI_CMDLINE_LIST;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	*next = option;
114*4882a593Smuzhiyun 	return true;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
efi_parse_option_graphics(char * option)117*4882a593Smuzhiyun void efi_parse_option_graphics(char *option)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	while (*option) {
120*4882a593Smuzhiyun 		if (parse_modenum(option, &option))
121*4882a593Smuzhiyun 			continue;
122*4882a593Smuzhiyun 		if (parse_res(option, &option))
123*4882a593Smuzhiyun 			continue;
124*4882a593Smuzhiyun 		if (parse_auto(option, &option))
125*4882a593Smuzhiyun 			continue;
126*4882a593Smuzhiyun 		if (parse_list(option, &option))
127*4882a593Smuzhiyun 			continue;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 		while (*option && *option++ != ',')
130*4882a593Smuzhiyun 			;
131*4882a593Smuzhiyun 	}
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
choose_mode_modenum(efi_graphics_output_protocol_t * gop)134*4882a593Smuzhiyun static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	efi_status_t status;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
139*4882a593Smuzhiyun 	efi_graphics_output_mode_info_t *info;
140*4882a593Smuzhiyun 	unsigned long info_size;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	u32 max_mode, cur_mode;
143*4882a593Smuzhiyun 	int pf;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	cur_mode = efi_table_attr(mode, mode);
148*4882a593Smuzhiyun 	if (cmdline.mode == cur_mode)
149*4882a593Smuzhiyun 		return cur_mode;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	max_mode = efi_table_attr(mode, max_mode);
152*4882a593Smuzhiyun 	if (cmdline.mode >= max_mode) {
153*4882a593Smuzhiyun 		efi_err("Requested mode is invalid\n");
154*4882a593Smuzhiyun 		return cur_mode;
155*4882a593Smuzhiyun 	}
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	status = efi_call_proto(gop, query_mode, cmdline.mode,
158*4882a593Smuzhiyun 				&info_size, &info);
159*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
160*4882a593Smuzhiyun 		efi_err("Couldn't get mode information\n");
161*4882a593Smuzhiyun 		return cur_mode;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	pf = info->pixel_format;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	efi_bs_call(free_pool, info);
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
169*4882a593Smuzhiyun 		efi_err("Invalid PixelFormat\n");
170*4882a593Smuzhiyun 		return cur_mode;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	return cmdline.mode;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
pixel_bpp(int pixel_format,efi_pixel_bitmask_t pixel_info)176*4882a593Smuzhiyun static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	if (pixel_format == PIXEL_BIT_MASK) {
179*4882a593Smuzhiyun 		u32 mask = pixel_info.red_mask | pixel_info.green_mask |
180*4882a593Smuzhiyun 			   pixel_info.blue_mask | pixel_info.reserved_mask;
181*4882a593Smuzhiyun 		if (!mask)
182*4882a593Smuzhiyun 			return 0;
183*4882a593Smuzhiyun 		return __fls(mask) - __ffs(mask) + 1;
184*4882a593Smuzhiyun 	} else
185*4882a593Smuzhiyun 		return 32;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
choose_mode_res(efi_graphics_output_protocol_t * gop)188*4882a593Smuzhiyun static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	efi_status_t status;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
193*4882a593Smuzhiyun 	efi_graphics_output_mode_info_t *info;
194*4882a593Smuzhiyun 	unsigned long info_size;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	u32 max_mode, cur_mode;
197*4882a593Smuzhiyun 	int pf;
198*4882a593Smuzhiyun 	efi_pixel_bitmask_t pi;
199*4882a593Smuzhiyun 	u32 m, w, h;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	cur_mode = efi_table_attr(mode, mode);
204*4882a593Smuzhiyun 	info = efi_table_attr(mode, info);
205*4882a593Smuzhiyun 	pf = info->pixel_format;
206*4882a593Smuzhiyun 	pi = info->pixel_information;
207*4882a593Smuzhiyun 	w  = info->horizontal_resolution;
208*4882a593Smuzhiyun 	h  = info->vertical_resolution;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (w == cmdline.res.width && h == cmdline.res.height &&
211*4882a593Smuzhiyun 	    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
212*4882a593Smuzhiyun 	    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
213*4882a593Smuzhiyun 		return cur_mode;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	max_mode = efi_table_attr(mode, max_mode);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	for (m = 0; m < max_mode; m++) {
218*4882a593Smuzhiyun 		if (m == cur_mode)
219*4882a593Smuzhiyun 			continue;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 		status = efi_call_proto(gop, query_mode, m,
222*4882a593Smuzhiyun 					&info_size, &info);
223*4882a593Smuzhiyun 		if (status != EFI_SUCCESS)
224*4882a593Smuzhiyun 			continue;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 		pf = info->pixel_format;
227*4882a593Smuzhiyun 		pi = info->pixel_information;
228*4882a593Smuzhiyun 		w  = info->horizontal_resolution;
229*4882a593Smuzhiyun 		h  = info->vertical_resolution;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 		efi_bs_call(free_pool, info);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
234*4882a593Smuzhiyun 			continue;
235*4882a593Smuzhiyun 		if (w == cmdline.res.width && h == cmdline.res.height &&
236*4882a593Smuzhiyun 		    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
237*4882a593Smuzhiyun 		    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
238*4882a593Smuzhiyun 			return m;
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	efi_err("Couldn't find requested mode\n");
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	return cur_mode;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
choose_mode_auto(efi_graphics_output_protocol_t * gop)246*4882a593Smuzhiyun static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun 	efi_status_t status;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
251*4882a593Smuzhiyun 	efi_graphics_output_mode_info_t *info;
252*4882a593Smuzhiyun 	unsigned long info_size;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	u32 max_mode, cur_mode, best_mode, area;
255*4882a593Smuzhiyun 	u8 depth;
256*4882a593Smuzhiyun 	int pf;
257*4882a593Smuzhiyun 	efi_pixel_bitmask_t pi;
258*4882a593Smuzhiyun 	u32 m, w, h, a;
259*4882a593Smuzhiyun 	u8 d;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	cur_mode = efi_table_attr(mode, mode);
264*4882a593Smuzhiyun 	max_mode = efi_table_attr(mode, max_mode);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	info = efi_table_attr(mode, info);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	pf = info->pixel_format;
269*4882a593Smuzhiyun 	pi = info->pixel_information;
270*4882a593Smuzhiyun 	w  = info->horizontal_resolution;
271*4882a593Smuzhiyun 	h  = info->vertical_resolution;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	best_mode = cur_mode;
274*4882a593Smuzhiyun 	area = w * h;
275*4882a593Smuzhiyun 	depth = pixel_bpp(pf, pi);
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	for (m = 0; m < max_mode; m++) {
278*4882a593Smuzhiyun 		if (m == cur_mode)
279*4882a593Smuzhiyun 			continue;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 		status = efi_call_proto(gop, query_mode, m,
282*4882a593Smuzhiyun 					&info_size, &info);
283*4882a593Smuzhiyun 		if (status != EFI_SUCCESS)
284*4882a593Smuzhiyun 			continue;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		pf = info->pixel_format;
287*4882a593Smuzhiyun 		pi = info->pixel_information;
288*4882a593Smuzhiyun 		w  = info->horizontal_resolution;
289*4882a593Smuzhiyun 		h  = info->vertical_resolution;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 		efi_bs_call(free_pool, info);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
294*4882a593Smuzhiyun 			continue;
295*4882a593Smuzhiyun 		a = w * h;
296*4882a593Smuzhiyun 		if (a < area)
297*4882a593Smuzhiyun 			continue;
298*4882a593Smuzhiyun 		d = pixel_bpp(pf, pi);
299*4882a593Smuzhiyun 		if (a > area || d > depth) {
300*4882a593Smuzhiyun 			best_mode = m;
301*4882a593Smuzhiyun 			area = a;
302*4882a593Smuzhiyun 			depth = d;
303*4882a593Smuzhiyun 		}
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	return best_mode;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun 
choose_mode_list(efi_graphics_output_protocol_t * gop)309*4882a593Smuzhiyun static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	efi_status_t status;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
314*4882a593Smuzhiyun 	efi_graphics_output_mode_info_t *info;
315*4882a593Smuzhiyun 	unsigned long info_size;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	u32 max_mode, cur_mode;
318*4882a593Smuzhiyun 	int pf;
319*4882a593Smuzhiyun 	efi_pixel_bitmask_t pi;
320*4882a593Smuzhiyun 	u32 m, w, h;
321*4882a593Smuzhiyun 	u8 d;
322*4882a593Smuzhiyun 	const char *dstr;
323*4882a593Smuzhiyun 	bool valid;
324*4882a593Smuzhiyun 	efi_input_key_t key;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	cur_mode = efi_table_attr(mode, mode);
329*4882a593Smuzhiyun 	max_mode = efi_table_attr(mode, max_mode);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
332*4882a593Smuzhiyun 	efi_puts("  * = current mode\n"
333*4882a593Smuzhiyun 		 "  - = unusable mode\n");
334*4882a593Smuzhiyun 	for (m = 0; m < max_mode; m++) {
335*4882a593Smuzhiyun 		status = efi_call_proto(gop, query_mode, m,
336*4882a593Smuzhiyun 					&info_size, &info);
337*4882a593Smuzhiyun 		if (status != EFI_SUCCESS)
338*4882a593Smuzhiyun 			continue;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 		pf = info->pixel_format;
341*4882a593Smuzhiyun 		pi = info->pixel_information;
342*4882a593Smuzhiyun 		w  = info->horizontal_resolution;
343*4882a593Smuzhiyun 		h  = info->vertical_resolution;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 		efi_bs_call(free_pool, info);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 		valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
348*4882a593Smuzhiyun 		d = 0;
349*4882a593Smuzhiyun 		switch (pf) {
350*4882a593Smuzhiyun 		case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
351*4882a593Smuzhiyun 			dstr = "rgb";
352*4882a593Smuzhiyun 			break;
353*4882a593Smuzhiyun 		case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
354*4882a593Smuzhiyun 			dstr = "bgr";
355*4882a593Smuzhiyun 			break;
356*4882a593Smuzhiyun 		case PIXEL_BIT_MASK:
357*4882a593Smuzhiyun 			dstr = "";
358*4882a593Smuzhiyun 			d = pixel_bpp(pf, pi);
359*4882a593Smuzhiyun 			break;
360*4882a593Smuzhiyun 		case PIXEL_BLT_ONLY:
361*4882a593Smuzhiyun 			dstr = "blt";
362*4882a593Smuzhiyun 			break;
363*4882a593Smuzhiyun 		default:
364*4882a593Smuzhiyun 			dstr = "xxx";
365*4882a593Smuzhiyun 			break;
366*4882a593Smuzhiyun 		}
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 		efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
369*4882a593Smuzhiyun 			   m,
370*4882a593Smuzhiyun 			   m == cur_mode ? '*' : ' ',
371*4882a593Smuzhiyun 			   !valid ? '-' : ' ',
372*4882a593Smuzhiyun 			   w, h, dstr, d);
373*4882a593Smuzhiyun 	}
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
376*4882a593Smuzhiyun 	status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
377*4882a593Smuzhiyun 	if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
378*4882a593Smuzhiyun 		efi_err("Unable to read key, continuing in 10 seconds\n");
379*4882a593Smuzhiyun 		efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
380*4882a593Smuzhiyun 	}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	return cur_mode;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
set_mode(efi_graphics_output_protocol_t * gop)385*4882a593Smuzhiyun static void set_mode(efi_graphics_output_protocol_t *gop)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
388*4882a593Smuzhiyun 	u32 cur_mode, new_mode;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	switch (cmdline.option) {
391*4882a593Smuzhiyun 	case EFI_CMDLINE_MODE_NUM:
392*4882a593Smuzhiyun 		new_mode = choose_mode_modenum(gop);
393*4882a593Smuzhiyun 		break;
394*4882a593Smuzhiyun 	case EFI_CMDLINE_RES:
395*4882a593Smuzhiyun 		new_mode = choose_mode_res(gop);
396*4882a593Smuzhiyun 		break;
397*4882a593Smuzhiyun 	case EFI_CMDLINE_AUTO:
398*4882a593Smuzhiyun 		new_mode = choose_mode_auto(gop);
399*4882a593Smuzhiyun 		break;
400*4882a593Smuzhiyun 	case EFI_CMDLINE_LIST:
401*4882a593Smuzhiyun 		new_mode = choose_mode_list(gop);
402*4882a593Smuzhiyun 		break;
403*4882a593Smuzhiyun 	default:
404*4882a593Smuzhiyun 		return;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
408*4882a593Smuzhiyun 	cur_mode = efi_table_attr(mode, mode);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	if (new_mode == cur_mode)
411*4882a593Smuzhiyun 		return;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
414*4882a593Smuzhiyun 		efi_err("Failed to set requested mode\n");
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun 
find_bits(u32 mask,u8 * pos,u8 * size)417*4882a593Smuzhiyun static void find_bits(u32 mask, u8 *pos, u8 *size)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun 	if (!mask) {
420*4882a593Smuzhiyun 		*pos = *size = 0;
421*4882a593Smuzhiyun 		return;
422*4882a593Smuzhiyun 	}
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	/* UEFI spec guarantees that the set bits are contiguous */
425*4882a593Smuzhiyun 	*pos  = __ffs(mask);
426*4882a593Smuzhiyun 	*size = __fls(mask) - *pos + 1;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun static void
setup_pixel_info(struct screen_info * si,u32 pixels_per_scan_line,efi_pixel_bitmask_t pixel_info,int pixel_format)430*4882a593Smuzhiyun setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
431*4882a593Smuzhiyun 		 efi_pixel_bitmask_t pixel_info, int pixel_format)
432*4882a593Smuzhiyun {
433*4882a593Smuzhiyun 	if (pixel_format == PIXEL_BIT_MASK) {
434*4882a593Smuzhiyun 		find_bits(pixel_info.red_mask,
435*4882a593Smuzhiyun 			  &si->red_pos, &si->red_size);
436*4882a593Smuzhiyun 		find_bits(pixel_info.green_mask,
437*4882a593Smuzhiyun 			  &si->green_pos, &si->green_size);
438*4882a593Smuzhiyun 		find_bits(pixel_info.blue_mask,
439*4882a593Smuzhiyun 			  &si->blue_pos, &si->blue_size);
440*4882a593Smuzhiyun 		find_bits(pixel_info.reserved_mask,
441*4882a593Smuzhiyun 			  &si->rsvd_pos, &si->rsvd_size);
442*4882a593Smuzhiyun 		si->lfb_depth = si->red_size + si->green_size +
443*4882a593Smuzhiyun 			si->blue_size + si->rsvd_size;
444*4882a593Smuzhiyun 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
445*4882a593Smuzhiyun 	} else {
446*4882a593Smuzhiyun 		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
447*4882a593Smuzhiyun 			si->red_pos   = 0;
448*4882a593Smuzhiyun 			si->blue_pos  = 16;
449*4882a593Smuzhiyun 		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
450*4882a593Smuzhiyun 			si->blue_pos  = 0;
451*4882a593Smuzhiyun 			si->red_pos   = 16;
452*4882a593Smuzhiyun 		}
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 		si->green_pos = 8;
455*4882a593Smuzhiyun 		si->rsvd_pos  = 24;
456*4882a593Smuzhiyun 		si->red_size = si->green_size =
457*4882a593Smuzhiyun 			si->blue_size = si->rsvd_size = 8;
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 		si->lfb_depth = 32;
460*4882a593Smuzhiyun 		si->lfb_linelength = pixels_per_scan_line * 4;
461*4882a593Smuzhiyun 	}
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun static efi_graphics_output_protocol_t *
find_gop(efi_guid_t * proto,unsigned long size,void ** handles)465*4882a593Smuzhiyun find_gop(efi_guid_t *proto, unsigned long size, void **handles)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun 	efi_graphics_output_protocol_t *first_gop;
468*4882a593Smuzhiyun 	efi_handle_t h;
469*4882a593Smuzhiyun 	int i;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	first_gop = NULL;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	for_each_efi_handle(h, handles, size, i) {
474*4882a593Smuzhiyun 		efi_status_t status;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 		efi_graphics_output_protocol_t *gop;
477*4882a593Smuzhiyun 		efi_graphics_output_protocol_mode_t *mode;
478*4882a593Smuzhiyun 		efi_graphics_output_mode_info_t *info;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
481*4882a593Smuzhiyun 		void *dummy = NULL;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
484*4882a593Smuzhiyun 		if (status != EFI_SUCCESS)
485*4882a593Smuzhiyun 			continue;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 		mode = efi_table_attr(gop, mode);
488*4882a593Smuzhiyun 		info = efi_table_attr(mode, info);
489*4882a593Smuzhiyun 		if (info->pixel_format == PIXEL_BLT_ONLY ||
490*4882a593Smuzhiyun 		    info->pixel_format >= PIXEL_FORMAT_MAX)
491*4882a593Smuzhiyun 			continue;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 		/*
494*4882a593Smuzhiyun 		 * Systems that use the UEFI Console Splitter may
495*4882a593Smuzhiyun 		 * provide multiple GOP devices, not all of which are
496*4882a593Smuzhiyun 		 * backed by real hardware. The workaround is to search
497*4882a593Smuzhiyun 		 * for a GOP implementing the ConOut protocol, and if
498*4882a593Smuzhiyun 		 * one isn't found, to just fall back to the first GOP.
499*4882a593Smuzhiyun 		 *
500*4882a593Smuzhiyun 		 * Once we've found a GOP supporting ConOut,
501*4882a593Smuzhiyun 		 * don't bother looking any further.
502*4882a593Smuzhiyun 		 */
503*4882a593Smuzhiyun 		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
504*4882a593Smuzhiyun 		if (status == EFI_SUCCESS)
505*4882a593Smuzhiyun 			return gop;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 		if (!first_gop)
508*4882a593Smuzhiyun 			first_gop = gop;
509*4882a593Smuzhiyun 	}
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	return first_gop;
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size,void ** handles)514*4882a593Smuzhiyun static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
515*4882a593Smuzhiyun 			      unsigned long size, void **handles)
516*4882a593Smuzhiyun {
517*4882a593Smuzhiyun 	efi_graphics_output_protocol_t *gop;
518*4882a593Smuzhiyun 	efi_graphics_output_protocol_mode_t *mode;
519*4882a593Smuzhiyun 	efi_graphics_output_mode_info_t *info;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	gop = find_gop(proto, size, handles);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	/* Did we find any GOPs? */
524*4882a593Smuzhiyun 	if (!gop)
525*4882a593Smuzhiyun 		return EFI_NOT_FOUND;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	/* Change mode if requested */
528*4882a593Smuzhiyun 	set_mode(gop);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	/* EFI framebuffer */
531*4882a593Smuzhiyun 	mode = efi_table_attr(gop, mode);
532*4882a593Smuzhiyun 	info = efi_table_attr(mode, info);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	si->lfb_width  = info->horizontal_resolution;
537*4882a593Smuzhiyun 	si->lfb_height = info->vertical_resolution;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
540*4882a593Smuzhiyun 			  &si->lfb_base, &si->ext_lfb_base);
541*4882a593Smuzhiyun 	if (si->ext_lfb_base)
542*4882a593Smuzhiyun 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	si->pages = 1;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	setup_pixel_info(si, info->pixels_per_scan_line,
547*4882a593Smuzhiyun 			     info->pixel_information, info->pixel_format);
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	si->lfb_size = si->lfb_linelength * si->lfb_height;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	return EFI_SUCCESS;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun /*
557*4882a593Smuzhiyun  * See if we have Graphics Output Protocol
558*4882a593Smuzhiyun  */
efi_setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size)559*4882a593Smuzhiyun efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
560*4882a593Smuzhiyun 			   unsigned long size)
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun 	efi_status_t status;
563*4882a593Smuzhiyun 	void **gop_handle = NULL;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
566*4882a593Smuzhiyun 			     (void **)&gop_handle);
567*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
568*4882a593Smuzhiyun 		return status;
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
571*4882a593Smuzhiyun 			     &size, gop_handle);
572*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
573*4882a593Smuzhiyun 		goto free_handle;
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	status = setup_gop(si, proto, size, gop_handle);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun free_handle:
578*4882a593Smuzhiyun 	efi_bs_call(free_pool, gop_handle);
579*4882a593Smuzhiyun 	return status;
580*4882a593Smuzhiyun }
581