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