1/* 2 * Copyright 2016, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "video.h" 8 9#include <stdlib.h> 10 11#include <boot/kernel_args.h> 12#include <boot/menu.h> 13#include <boot/platform.h> 14#include <boot/platform/generic/video.h> 15#include <boot/stage2.h> 16#include <boot/stdio.h> 17#include <drivers/driver_settings.h> 18#include <edid.h> 19#include <util/list.h> 20 21#include "efi_platform.h" 22#include <efi/protocol/edid.h> 23#include <efi/protocol/graphics-output.h> 24 25 26//#define TRACE_VIDEO 27#ifdef TRACE_VIDEO 28# define TRACE(x) dprintf x 29#else 30# define TRACE(x) ; 31#endif 32 33 34struct video_mode { 35 list_link link; 36 size_t mode; 37 size_t width, height, bits_per_pixel, bytes_per_row; 38}; 39 40 41static efi_guid sGraphicsOutputGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 42static efi_guid sEdidActiveGuid = EFI_EDID_ACTIVE_PROTOCOL_GUID; 43static efi_graphics_output_protocol *sGraphicsOutput; 44static efi_edid_protocol *sEdidActiveProtocol; 45static size_t sGraphicsMode; 46static struct list sModeList; 47static uint32 sModeCount; 48static bool sModeChosen; 49static bool sSettingsLoaded; 50 51 52static int 53compare_video_modes(video_mode *a, video_mode *b) 54{ 55 int compare = a->width - b->width; 56 if (compare != 0) 57 return compare; 58 59 compare = a->height - b->height; 60 if (compare != 0) 61 return compare; 62 63 return a->bits_per_pixel - b->bits_per_pixel; 64} 65 66 67static void 68add_video_mode(video_mode *videoMode) 69{ 70 video_mode *mode = NULL; 71 while ((mode = (video_mode*)list_get_next_item(&sModeList, mode)) 72 != NULL) { 73 int compare = compare_video_modes(videoMode, mode); 74 if (compare == 0) { 75 // mode already exists 76 return; 77 } 78 79 if (compare > 0) 80 break; 81 } 82 83 list_insert_item_before(&sModeList, mode, videoMode); 84 sModeCount++; 85} 86 87 88static video_mode* 89closest_video_mode(uint32 width, uint32 height, uint32 depth) 90{ 91 video_mode *bestMode = NULL; 92 int64 bestDiff = 0; 93 94 video_mode *mode = NULL; 95 while ((mode = (video_mode*)list_get_next_item(&sModeList, mode)) != NULL) { 96 if (mode->width > width) { 97 // Only choose modes with a width less or equal than the searched 98 // one; or else it might well be that the monitor cannot keep up. 99 continue; 100 } 101 102 int64 diff = 2 * abs((int64)mode->width - width) 103 + abs((int64)mode->height - height) 104 + abs((int64)mode->bits_per_pixel - depth); 105 106 if (bestMode == NULL || bestDiff > diff) { 107 bestMode = mode; 108 bestDiff = diff; 109 } 110 } 111 112 return bestMode; 113} 114 115 116static void 117get_mode_from_settings(void) 118{ 119 if (sSettingsLoaded) 120 return; 121 122 void *handle = load_driver_settings("vesa"); 123 if (handle == NULL) 124 return; 125 126 const driver_settings *settings = get_driver_settings(handle); 127 if (settings == NULL) 128 goto out; 129 130 sSettingsLoaded = true; 131 132 for (int32 i = 0; i < settings->parameter_count; i++) { 133 driver_parameter ¶meter = settings->parameters[i]; 134 135 if (parameter.value_count < 3 || strcmp(parameter.name, "mode") != 0) continue; 136 uint32 width = strtoul(parameter.values[0], NULL, 0); 137 uint32 height = strtoul(parameter.values[1], NULL, 0); 138 uint32 depth = strtoul(parameter.values[2], NULL, 0); 139 140 // search mode that fits 141 video_mode *mode = closest_video_mode(width, height, depth); 142 if (mode != NULL) { 143 sGraphicsMode = mode->mode; 144 break; 145 } 146 } 147 148out: 149 unload_driver_settings(handle); 150} 151 152 153extern "C" status_t 154platform_init_video(void) 155{ 156 list_init(&sModeList); 157 158 // we don't support VESA modes 159 gKernelArgs.vesa_modes = NULL; 160 gKernelArgs.vesa_modes_size = 0; 161 162 gKernelArgs.edid_info = NULL; 163 164 // make a guess at the best video mode to use, and save the mode ID for switching to graphics 165 // mode 166 efi_status status = kBootServices->LocateProtocol(&sGraphicsOutputGuid, NULL, 167 (void **)&sGraphicsOutput); 168 if (sGraphicsOutput == NULL || status != EFI_SUCCESS) { 169 dprintf("GOP protocol not found\n"); 170 gKernelArgs.frame_buffer.enabled = false; 171 sGraphicsOutput = NULL; 172 return B_ERROR; 173 } 174 175 size_t bestArea = 0; 176 size_t bestDepth = 0; 177 178 TRACE(("looking for best graphics mode...\n")); 179 180 for (size_t mode = 0; mode < sGraphicsOutput->Mode->MaxMode; ++mode) { 181 efi_graphics_output_mode_information *info; 182 size_t size, depth; 183 sGraphicsOutput->QueryMode(sGraphicsOutput, mode, &size, &info); 184 size_t area = info->HorizontalResolution * info->VerticalResolution; 185 TRACE((" mode: %lu\n", mode)); 186 TRACE((" width: %u\n", info->HorizontalResolution)); 187 TRACE((" height: %u\n", info->VerticalResolution)); 188 TRACE((" area: %lu\n", area)); 189 if (info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) { 190 depth = 32; 191 } else if (info->PixelFormat == PixelBlueGreenRedReserved8BitPerColor) { 192 // seen this in the wild, but acts like RGB, go figure... 193 depth = 32; 194 } else if (info->PixelFormat == PixelBitMask 195 && info->PixelInformation.RedMask == 0xFF0000 196 && info->PixelInformation.GreenMask == 0x00FF00 197 && info->PixelInformation.BlueMask == 0x0000FF 198 && info->PixelInformation.ReservedMask == 0) { 199 depth = 24; 200 } else { 201 TRACE((" pixel format: %x unsupported\n", 202 info->PixelFormat)); 203 continue; 204 } 205 TRACE((" depth: %lu\n", depth)); 206 207 video_mode *videoMode = (video_mode*)malloc(sizeof(struct video_mode)); 208 if (videoMode != NULL) { 209 videoMode->mode = mode; 210 videoMode->width = info->HorizontalResolution; 211 videoMode->height = info->VerticalResolution; 212 videoMode->bits_per_pixel = info->PixelFormat == PixelBitMask ? 24 : 32; 213 videoMode->bytes_per_row = info->PixelsPerScanLine * depth / 8; 214 add_video_mode(videoMode); 215 } 216 217 area *= depth; 218 TRACE((" area (w/depth): %lu\n", area)); 219 if (area >= bestArea) { 220 TRACE(("selected new best mode: %lu\n", mode)); 221 bestArea = area; 222 bestDepth = depth; 223 sGraphicsMode = mode; 224 } 225 } 226 227 if (bestArea == 0 || bestDepth == 0) { 228 sGraphicsOutput = NULL; 229 gKernelArgs.frame_buffer.enabled = false; 230 return B_ERROR; 231 } 232 233 gKernelArgs.frame_buffer.enabled = true; 234 sModeChosen = false; 235 sSettingsLoaded = false; 236 237 status = kBootServices->LocateProtocol(&sEdidActiveGuid, NULL, (void **)&sEdidActiveProtocol); 238 if ((sEdidActiveProtocol != NULL) && (status == EFI_SUCCESS) 239 && (sEdidActiveProtocol->SizeOfEdid) != 0) { 240 edid1_info* edid_info = (edid1_info*)kernel_args_malloc(sizeof(edid1_info)); 241 if (edid_info != NULL) { 242 edid_decode(edid_info, (edid1_raw*)sEdidActiveProtocol->Edid); 243 gKernelArgs.edid_info = edid_info; 244 } 245 } 246 247 return B_OK; 248} 249 250 251extern "C" void 252platform_switch_to_logo(void) 253{ 254 if (sGraphicsOutput == NULL || !gKernelArgs.frame_buffer.enabled) 255 return; 256 257 if (!sModeChosen) 258 get_mode_from_settings(); 259 260 sGraphicsOutput->SetMode(sGraphicsOutput, sGraphicsMode); 261 gKernelArgs.frame_buffer.physical_buffer.start = 262 sGraphicsOutput->Mode->FrameBufferBase; 263 gKernelArgs.frame_buffer.physical_buffer.size = 264 sGraphicsOutput->Mode->FrameBufferSize; 265 gKernelArgs.frame_buffer.width = 266 sGraphicsOutput->Mode->Info->HorizontalResolution; 267 gKernelArgs.frame_buffer.height = 268 sGraphicsOutput->Mode->Info->VerticalResolution; 269 gKernelArgs.frame_buffer.depth = 270 sGraphicsOutput->Mode->Info->PixelFormat == PixelBitMask ? 24 : 32; 271 gKernelArgs.frame_buffer.bytes_per_row = 272 sGraphicsOutput->Mode->Info->PixelsPerScanLine 273 * gKernelArgs.frame_buffer.depth / 8; 274 275 video_display_splash(gKernelArgs.frame_buffer.physical_buffer.start); 276} 277 278 279bool 280video_mode_hook(Menu *menu, MenuItem *item) 281{ 282 Menu* submenu = item->Submenu(); 283 MenuItem* subitem = submenu->FindMarked(); 284 if (subitem != NULL) { 285 sGraphicsMode = (size_t)subitem->Data(); 286 sModeChosen = true; 287 } 288 289 return true; 290} 291 292 293Menu* 294video_mode_menu() 295{ 296 Menu *menu = new(std::nothrow)Menu(CHOICE_MENU, "Select Video Mode"); 297 MenuItem *item; 298 299 video_mode *mode = NULL; 300 while ((mode = (video_mode*)list_get_next_item(&sModeList, mode)) != NULL) { 301 char label[64]; 302 snprintf(label, sizeof(label), "%lux%lu %lu bit", mode->width, 303 mode->height, mode->bits_per_pixel); 304 305 menu->AddItem(item = new (std::nothrow)MenuItem(label)); 306 item->SetData((const void*)mode->mode); 307 if (mode->mode == sGraphicsMode) { 308 item->SetMarked(true); 309 item->Select(true); 310 } 311 } 312 313 menu->AddSeparatorItem(); 314 menu->AddItem(item = new(std::nothrow)MenuItem("Return to main menu")); 315 item->SetType(MENU_ITEM_NO_CHOICE); 316 317 return menu; 318} 319 320 321extern "C" void 322platform_blit4(addr_t frameBuffer, const uint8 *data, 323 uint16 width, uint16 height, uint16 imageWidth, 324 uint16 left, uint16 top) 325{ 326 panic("platform_blit4 unsupported"); 327 return; 328} 329 330 331extern "C" void 332platform_set_palette(const uint8 *palette) 333{ 334 panic("platform_set_palette unsupported"); 335 return; 336} 337