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