]>
Commit | Line | Data |
---|---|---|
4a08c746 BM |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018, Bin Meng <[email protected]> | |
4 | * | |
5 | * EFI framebuffer driver based on GOP | |
6 | */ | |
7 | ||
644e6144 SG |
8 | #define LOG_CATEGORY LOGC_EFI |
9 | ||
4a08c746 BM |
10 | #include <dm.h> |
11 | #include <efi_api.h> | |
f7ae49fc | 12 | #include <log.h> |
cafe8712 | 13 | #include <vesa.h> |
4a08c746 BM |
14 | #include <video.h> |
15 | ||
16 | struct pixel { | |
17 | u8 pos; | |
18 | u8 size; | |
19 | }; | |
20 | ||
21 | static const struct efi_framebuffer { | |
22 | struct pixel red; | |
23 | struct pixel green; | |
24 | struct pixel blue; | |
25 | struct pixel rsvd; | |
26 | } efi_framebuffer_format_map[] = { | |
27 | [EFI_GOT_RGBA8] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} }, | |
28 | [EFI_GOT_BGRA8] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} }, | |
29 | }; | |
30 | ||
31 | static void efi_find_pixel_bits(u32 mask, u8 *pos, u8 *size) | |
32 | { | |
33 | u8 first, len; | |
34 | ||
35 | first = 0; | |
36 | len = 0; | |
37 | ||
38 | if (mask) { | |
39 | while (!(mask & 0x1)) { | |
40 | mask = mask >> 1; | |
41 | first++; | |
42 | } | |
43 | ||
44 | while (mask & 0x1) { | |
45 | mask = mask >> 1; | |
46 | len++; | |
47 | } | |
48 | } | |
49 | ||
50 | *pos = first; | |
51 | *size = len; | |
52 | } | |
53 | ||
88753816 SG |
54 | /** |
55 | * get_mode_info() - Ask EFI for the mode information | |
56 | * | |
57 | * Gets info from the graphics-output protocol | |
58 | * | |
59 | * @vesa: Place to put the mode information | |
644e6144 | 60 | * @fbp: Returns the address of the frame buffer |
57fa4182 | 61 | * @infop: Returns a pointer to the mode info |
88753816 SG |
62 | * Returns: 0 if OK, -ENOSYS if boot services are not available, -ENOTSUPP if |
63 | * the protocol is not supported by EFI | |
64 | */ | |
644e6144 | 65 | static int get_mode_info(struct vesa_mode_info *vesa, u64 *fbp, |
57fa4182 | 66 | struct efi_gop_mode_info **infop) |
1834c081 SG |
67 | { |
68 | efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; | |
69 | struct efi_boot_services *boot = efi_get_boot(); | |
70 | struct efi_gop_mode *mode; | |
71 | struct efi_gop *gop; | |
72 | int ret; | |
73 | ||
74 | if (!boot) | |
75 | return log_msg_ret("sys", -ENOSYS); | |
76 | ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); | |
77 | if (ret) | |
78 | return log_msg_ret("prot", -ENOTSUPP); | |
1834c081 | 79 | mode = gop->mode; |
644e6144 SG |
80 | log_debug("maxmode %u, mode %u, info %p, size %lx, fb %lx, fb_size %lx\n", |
81 | mode->max_mode, mode->mode, mode->info, mode->info_size, | |
82 | (ulong)mode->fb_base, (ulong)mode->fb_size); | |
83 | ||
1834c081 | 84 | vesa->phys_base_ptr = mode->fb_base; |
644e6144 | 85 | *fbp = mode->fb_base; |
1834c081 SG |
86 | vesa->x_resolution = mode->info->width; |
87 | vesa->y_resolution = mode->info->height; | |
57fa4182 | 88 | *infop = mode->info; |
1834c081 SG |
89 | |
90 | return 0; | |
91 | } | |
92 | ||
88753816 SG |
93 | /** |
94 | * get_mode_from_entry() - Obtain fb info from the EFIET_GOP_MODE payload entry | |
95 | * | |
96 | * This gets the mode information provided by the stub to the payload and puts | |
97 | * it into a vesa structure. It also returns the mode information. | |
98 | * | |
99 | * @vesa: Place to put the mode information | |
644e6144 | 100 | * @fbp: Returns the address of the frame buffer |
88753816 SG |
101 | * @infop: Returns a pointer to the mode info |
102 | * Returns: 0 if OK, -ve on error | |
103 | */ | |
644e6144 | 104 | static int get_mode_from_entry(struct vesa_mode_info *vesa, u64 *fbp, |
88753816 SG |
105 | struct efi_gop_mode_info **infop) |
106 | { | |
107 | struct efi_gop_mode *mode; | |
108 | int size; | |
109 | int ret; | |
110 | ||
111 | ret = efi_info_get(EFIET_GOP_MODE, (void **)&mode, &size); | |
112 | if (ret) { | |
113 | printf("EFI graphics output entry not found\n"); | |
114 | return ret; | |
115 | } | |
116 | vesa->phys_base_ptr = mode->fb_base; | |
644e6144 | 117 | *fbp = mode->fb_base; |
88753816 SG |
118 | vesa->x_resolution = mode->info->width; |
119 | vesa->y_resolution = mode->info->height; | |
120 | *infop = mode->info; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
644e6144 | 125 | static int save_vesa_mode(struct vesa_mode_info *vesa, u64 *fbp) |
4a08c746 | 126 | { |
4a08c746 | 127 | const struct efi_framebuffer *fbinfo; |
88753816 | 128 | struct efi_gop_mode_info *info; |
4a08c746 BM |
129 | int ret; |
130 | ||
88753816 | 131 | if (IS_ENABLED(CONFIG_EFI_APP)) |
644e6144 | 132 | ret = get_mode_info(vesa, fbp, &info); |
88753816 | 133 | else |
644e6144 | 134 | ret = get_mode_from_entry(vesa, fbp, &info); |
88753816 SG |
135 | if (ret) { |
136 | printf("EFI graphics output protocol not found (err=%dE)\n", | |
137 | ret); | |
138 | return ret; | |
4a08c746 BM |
139 | } |
140 | ||
88753816 SG |
141 | if (info->pixel_format < EFI_GOT_BITMASK) { |
142 | fbinfo = &efi_framebuffer_format_map[info->pixel_format]; | |
4a08c746 BM |
143 | vesa->red_mask_size = fbinfo->red.size; |
144 | vesa->red_mask_pos = fbinfo->red.pos; | |
145 | vesa->green_mask_size = fbinfo->green.size; | |
146 | vesa->green_mask_pos = fbinfo->green.pos; | |
147 | vesa->blue_mask_size = fbinfo->blue.size; | |
148 | vesa->blue_mask_pos = fbinfo->blue.pos; | |
149 | vesa->reserved_mask_size = fbinfo->rsvd.size; | |
150 | vesa->reserved_mask_pos = fbinfo->rsvd.pos; | |
151 | ||
152 | vesa->bits_per_pixel = 32; | |
88753816 SG |
153 | vesa->bytes_per_scanline = info->pixels_per_scanline * 4; |
154 | } else if (info->pixel_format == EFI_GOT_BITMASK) { | |
155 | efi_find_pixel_bits(info->pixel_bitmask[0], | |
4a08c746 BM |
156 | &vesa->red_mask_pos, |
157 | &vesa->red_mask_size); | |
88753816 | 158 | efi_find_pixel_bits(info->pixel_bitmask[1], |
4a08c746 BM |
159 | &vesa->green_mask_pos, |
160 | &vesa->green_mask_size); | |
88753816 | 161 | efi_find_pixel_bits(info->pixel_bitmask[2], |
4a08c746 BM |
162 | &vesa->blue_mask_pos, |
163 | &vesa->blue_mask_size); | |
88753816 | 164 | efi_find_pixel_bits(info->pixel_bitmask[3], |
4a08c746 BM |
165 | &vesa->reserved_mask_pos, |
166 | &vesa->reserved_mask_size); | |
167 | vesa->bits_per_pixel = vesa->red_mask_size + | |
168 | vesa->green_mask_size + | |
169 | vesa->blue_mask_size + | |
170 | vesa->reserved_mask_size; | |
88753816 | 171 | vesa->bytes_per_scanline = (info->pixels_per_scanline * |
4a08c746 BM |
172 | vesa->bits_per_pixel) / 8; |
173 | } else { | |
644e6144 | 174 | log_err("Unknown framebuffer format: %d\n", info->pixel_format); |
4a08c746 BM |
175 | return -EINVAL; |
176 | } | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static int efi_video_probe(struct udevice *dev) | |
182 | { | |
8a8d24bd | 183 | struct video_uc_plat *plat = dev_get_uclass_plat(dev); |
4a08c746 BM |
184 | struct video_priv *uc_priv = dev_get_uclass_priv(dev); |
185 | struct vesa_mode_info *vesa = &mode_info.vesa; | |
644e6144 | 186 | u64 fb; |
4a08c746 BM |
187 | int ret; |
188 | ||
189 | /* Initialize vesa_mode_info structure */ | |
644e6144 | 190 | ret = save_vesa_mode(vesa, &fb); |
4a08c746 BM |
191 | if (ret) |
192 | goto err; | |
193 | ||
644e6144 | 194 | ret = vesa_setup_video_priv(vesa, fb, uc_priv, plat); |
4a08c746 BM |
195 | if (ret) |
196 | goto err; | |
197 | ||
644e6144 SG |
198 | printf("Video: %dx%dx%d @ %lx\n", uc_priv->xsize, uc_priv->ysize, |
199 | vesa->bits_per_pixel, (ulong)fb); | |
4a08c746 BM |
200 | |
201 | return 0; | |
202 | ||
203 | err: | |
204 | printf("No video mode configured in EFI!\n"); | |
205 | return ret; | |
206 | } | |
207 | ||
f6222922 SG |
208 | static int efi_video_bind(struct udevice *dev) |
209 | { | |
210 | if (IS_ENABLED(CONFIG_VIDEO_COPY)) { | |
211 | struct video_uc_plat *plat = dev_get_uclass_plat(dev); | |
212 | struct vesa_mode_info vesa; | |
213 | int ret; | |
214 | u64 fb; | |
215 | ||
216 | /* | |
217 | * Initialise vesa_mode_info structure so we can figure out the | |
218 | * required framebuffer size. If something goes wrong, just do | |
219 | * without a copy framebuffer | |
220 | */ | |
221 | ret = save_vesa_mode(&vesa, &fb); | |
222 | if (!ret) { | |
223 | /* this is not reached if the EFI call failed */ | |
224 | plat->copy_size = vesa.bytes_per_scanline * | |
225 | vesa.y_resolution; | |
226 | } | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
4a08c746 BM |
232 | static const struct udevice_id efi_video_ids[] = { |
233 | { .compatible = "efi-fb" }, | |
234 | { } | |
235 | }; | |
236 | ||
237 | U_BOOT_DRIVER(efi_video) = { | |
238 | .name = "efi_video", | |
239 | .id = UCLASS_VIDEO, | |
240 | .of_match = efi_video_ids, | |
f6222922 | 241 | .bind = efi_video_bind, |
4a08c746 BM |
242 | .probe = efi_video_probe, |
243 | }; |