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