linux/drivers/firmware/efi/libstub/gop.c
<<
>>
Prefs
   1/* -----------------------------------------------------------------------
   2 *
   3 *   Copyright 2011 Intel Corporation; author Matt Fleming
   4 *
   5 *   This file is part of the Linux kernel, and is made available under
   6 *   the terms of the GNU General Public License version 2.
   7 *
   8 * ----------------------------------------------------------------------- */
   9
  10#include <linux/efi.h>
  11#include <linux/screen_info.h>
  12#include <asm/efi.h>
  13#include <asm/setup.h>
  14
  15static void find_bits(unsigned long mask, u8 *pos, u8 *size)
  16{
  17        u8 first, len;
  18
  19        first = 0;
  20        len = 0;
  21
  22        if (mask) {
  23                while (!(mask & 0x1)) {
  24                        mask = mask >> 1;
  25                        first++;
  26                }
  27
  28                while (mask & 0x1) {
  29                        mask = mask >> 1;
  30                        len++;
  31                }
  32        }
  33
  34        *pos = first;
  35        *size = len;
  36}
  37
  38static void
  39setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
  40                 struct efi_pixel_bitmask pixel_info, int pixel_format)
  41{
  42        if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
  43                si->lfb_depth = 32;
  44                si->lfb_linelength = pixels_per_scan_line * 4;
  45                si->red_size = 8;
  46                si->red_pos = 0;
  47                si->green_size = 8;
  48                si->green_pos = 8;
  49                si->blue_size = 8;
  50                si->blue_pos = 16;
  51                si->rsvd_size = 8;
  52                si->rsvd_pos = 24;
  53        } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
  54                si->lfb_depth = 32;
  55                si->lfb_linelength = pixels_per_scan_line * 4;
  56                si->red_size = 8;
  57                si->red_pos = 16;
  58                si->green_size = 8;
  59                si->green_pos = 8;
  60                si->blue_size = 8;
  61                si->blue_pos = 0;
  62                si->rsvd_size = 8;
  63                si->rsvd_pos = 24;
  64        } else if (pixel_format == PIXEL_BIT_MASK) {
  65                find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
  66                find_bits(pixel_info.green_mask, &si->green_pos,
  67                          &si->green_size);
  68                find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
  69                find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
  70                          &si->rsvd_size);
  71                si->lfb_depth = si->red_size + si->green_size +
  72                        si->blue_size + si->rsvd_size;
  73                si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
  74        } else {
  75                si->lfb_depth = 4;
  76                si->lfb_linelength = si->lfb_width / 2;
  77                si->red_size = 0;
  78                si->red_pos = 0;
  79                si->green_size = 0;
  80                si->green_pos = 0;
  81                si->blue_size = 0;
  82                si->blue_pos = 0;
  83                si->rsvd_size = 0;
  84                si->rsvd_pos = 0;
  85        }
  86}
  87
  88static efi_status_t
  89__gop_query32(efi_system_table_t *sys_table_arg,
  90              struct efi_graphics_output_protocol_32 *gop32,
  91              struct efi_graphics_output_mode_info **info,
  92              unsigned long *size, u64 *fb_base)
  93{
  94        struct efi_graphics_output_protocol_mode_32 *mode;
  95        efi_graphics_output_protocol_query_mode query_mode;
  96        efi_status_t status;
  97        unsigned long m;
  98
  99        m = gop32->mode;
 100        mode = (struct efi_graphics_output_protocol_mode_32 *)m;
 101        query_mode = (void *)(unsigned long)gop32->query_mode;
 102
 103        status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
 104                                  info);
 105        if (status != EFI_SUCCESS)
 106                return status;
 107
 108        *fb_base = mode->frame_buffer_base;
 109        return status;
 110}
 111
 112static efi_status_t
 113setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
 114            efi_guid_t *proto, unsigned long size, void **gop_handle)
 115{
 116        struct efi_graphics_output_protocol_32 *gop32, *first_gop;
 117        unsigned long nr_gops;
 118        u16 width, height;
 119        u32 pixels_per_scan_line;
 120        u32 ext_lfb_base;
 121        u64 fb_base;
 122        struct efi_pixel_bitmask pixel_info;
 123        int pixel_format;
 124        efi_status_t status = EFI_NOT_FOUND;
 125        u32 *handles = (u32 *)(unsigned long)gop_handle;
 126        int i;
 127
 128        first_gop = NULL;
 129        gop32 = NULL;
 130
 131        nr_gops = size / sizeof(u32);
 132        for (i = 0; i < nr_gops; i++) {
 133                struct efi_graphics_output_mode_info *info = NULL;
 134                efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
 135                bool conout_found = false;
 136                void *dummy = NULL;
 137                efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
 138                u64 current_fb_base;
 139
 140                status = efi_call_early(handle_protocol, h,
 141                                        proto, (void **)&gop32);
 142                if (status != EFI_SUCCESS)
 143                        continue;
 144
 145                status = efi_call_early(handle_protocol, h,
 146                                        &conout_proto, &dummy);
 147                if (status == EFI_SUCCESS)
 148                        conout_found = true;
 149
 150                status = __gop_query32(sys_table_arg, gop32, &info, &size,
 151                                       &current_fb_base);
 152                if (status == EFI_SUCCESS && (!first_gop || conout_found) &&
 153                    info->pixel_format != PIXEL_BLT_ONLY) {
 154                        /*
 155                         * Systems that use the UEFI Console Splitter may
 156                         * provide multiple GOP devices, not all of which are
 157                         * backed by real hardware. The workaround is to search
 158                         * for a GOP implementing the ConOut protocol, and if
 159                         * one isn't found, to just fall back to the first GOP.
 160                         */
 161                        width = info->horizontal_resolution;
 162                        height = info->vertical_resolution;
 163                        pixel_format = info->pixel_format;
 164                        pixel_info = info->pixel_information;
 165                        pixels_per_scan_line = info->pixels_per_scan_line;
 166                        fb_base = current_fb_base;
 167
 168                        /*
 169                         * Once we've found a GOP supporting ConOut,
 170                         * don't bother looking any further.
 171                         */
 172                        first_gop = gop32;
 173                        if (conout_found)
 174                                break;
 175                }
 176        }
 177
 178        /* Did we find any GOPs? */
 179        if (!first_gop)
 180                goto out;
 181
 182        /* EFI framebuffer */
 183        si->orig_video_isVGA = VIDEO_TYPE_EFI;
 184
 185        si->lfb_width = width;
 186        si->lfb_height = height;
 187        si->lfb_base = fb_base;
 188
 189        ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
 190        if (ext_lfb_base) {
 191                si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
 192                si->ext_lfb_base = ext_lfb_base;
 193        }
 194
 195        si->pages = 1;
 196
 197        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
 198
 199        si->lfb_size = si->lfb_linelength * si->lfb_height;
 200
 201        si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
 202out:
 203        return status;
 204}
 205
 206static efi_status_t
 207__gop_query64(efi_system_table_t *sys_table_arg,
 208              struct efi_graphics_output_protocol_64 *gop64,
 209              struct efi_graphics_output_mode_info **info,
 210              unsigned long *size, u64 *fb_base)
 211{
 212        struct efi_graphics_output_protocol_mode_64 *mode;
 213        efi_graphics_output_protocol_query_mode query_mode;
 214        efi_status_t status;
 215        unsigned long m;
 216
 217        m = gop64->mode;
 218        mode = (struct efi_graphics_output_protocol_mode_64 *)m;
 219        query_mode = (void *)(unsigned long)gop64->query_mode;
 220
 221        status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
 222                                  info);
 223        if (status != EFI_SUCCESS)
 224                return status;
 225
 226        *fb_base = mode->frame_buffer_base;
 227        return status;
 228}
 229
 230static efi_status_t
 231setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
 232            efi_guid_t *proto, unsigned long size, void **gop_handle)
 233{
 234        struct efi_graphics_output_protocol_64 *gop64, *first_gop;
 235        unsigned long nr_gops;
 236        u16 width, height;
 237        u32 pixels_per_scan_line;
 238        u32 ext_lfb_base;
 239        u64 fb_base;
 240        struct efi_pixel_bitmask pixel_info;
 241        int pixel_format;
 242        efi_status_t status = EFI_NOT_FOUND;
 243        u64 *handles = (u64 *)(unsigned long)gop_handle;
 244        int i;
 245
 246        first_gop = NULL;
 247        gop64 = NULL;
 248
 249        nr_gops = size / sizeof(u64);
 250        for (i = 0; i < nr_gops; i++) {
 251                struct efi_graphics_output_mode_info *info = NULL;
 252                efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
 253                bool conout_found = false;
 254                void *dummy = NULL;
 255                efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
 256                u64 current_fb_base;
 257
 258                status = efi_call_early(handle_protocol, h,
 259                                        proto, (void **)&gop64);
 260                if (status != EFI_SUCCESS)
 261                        continue;
 262
 263                status = efi_call_early(handle_protocol, h,
 264                                        &conout_proto, &dummy);
 265                if (status == EFI_SUCCESS)
 266                        conout_found = true;
 267
 268                status = __gop_query64(sys_table_arg, gop64, &info, &size,
 269                                       &current_fb_base);
 270                if (status == EFI_SUCCESS && (!first_gop || conout_found) &&
 271                    info->pixel_format != PIXEL_BLT_ONLY) {
 272                        /*
 273                         * Systems that use the UEFI Console Splitter may
 274                         * provide multiple GOP devices, not all of which are
 275                         * backed by real hardware. The workaround is to search
 276                         * for a GOP implementing the ConOut protocol, and if
 277                         * one isn't found, to just fall back to the first GOP.
 278                         */
 279                        width = info->horizontal_resolution;
 280                        height = info->vertical_resolution;
 281                        pixel_format = info->pixel_format;
 282                        pixel_info = info->pixel_information;
 283                        pixels_per_scan_line = info->pixels_per_scan_line;
 284                        fb_base = current_fb_base;
 285
 286                        /*
 287                         * Once we've found a GOP supporting ConOut,
 288                         * don't bother looking any further.
 289                         */
 290                        first_gop = gop64;
 291                        if (conout_found)
 292                                break;
 293                }
 294        }
 295
 296        /* Did we find any GOPs? */
 297        if (!first_gop)
 298                goto out;
 299
 300        /* EFI framebuffer */
 301        si->orig_video_isVGA = VIDEO_TYPE_EFI;
 302
 303        si->lfb_width = width;
 304        si->lfb_height = height;
 305        si->lfb_base = fb_base;
 306
 307        ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
 308        if (ext_lfb_base) {
 309                si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
 310                si->ext_lfb_base = ext_lfb_base;
 311        }
 312
 313        si->pages = 1;
 314
 315        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
 316
 317        si->lfb_size = si->lfb_linelength * si->lfb_height;
 318
 319        si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
 320out:
 321        return status;
 322}
 323
 324/*
 325 * See if we have Graphics Output Protocol
 326 */
 327efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
 328                           struct screen_info *si, efi_guid_t *proto,
 329                           unsigned long size)
 330{
 331        efi_status_t status;
 332        void **gop_handle = NULL;
 333
 334        status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
 335                                size, (void **)&gop_handle);
 336        if (status != EFI_SUCCESS)
 337                return status;
 338
 339        status = efi_call_early(locate_handle,
 340                                EFI_LOCATE_BY_PROTOCOL,
 341                                proto, NULL, &size, gop_handle);
 342        if (status != EFI_SUCCESS)
 343                goto free_handle;
 344
 345        if (efi_is_64bit()) {
 346                status = setup_gop64(sys_table_arg, si, proto, size,
 347                                     gop_handle);
 348        } else {
 349                status = setup_gop32(sys_table_arg, si, proto, size,
 350                                     gop_handle);
 351        }
 352
 353free_handle:
 354        efi_call_early(free_pool, gop_handle);
 355        return status;
 356}
 357