1 /* 2 * Copyright 2005-2015, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include <compute_display_timing.h> 11 #include <create_display_modes.h> 12 13 #include "accelerant_protos.h" 14 #include "accelerant.h" 15 #include "utility.h" 16 #include "vesa_info.h" 17 18 19 //#define TRACE_MODE 20 #ifdef TRACE_MODE 21 extern "C" void _sPrintf(const char* format, ...); 22 # define TRACE(x) _sPrintf x 23 #else 24 # define TRACE(x) ; 25 #endif 26 27 28 struct nvidia_resolution { 29 int width; 30 int height; 31 }; 32 33 static const nvidia_resolution kNVidiaAllowedResolutions[] = { 34 { 1280, 720 }, 35 { 1280, 800 }, 36 { 1360, 768 }, 37 { 1400, 1050 }, 38 { 1440, 900 }, 39 { 1600, 900 }, 40 { 1600, 1200 }, 41 { 1680, 1050 }, 42 { 1920, 1080 }, 43 { 1920, 1200 }, 44 { 2048, 1536 }, 45 }; 46 47 48 static uint32 49 get_color_space_for_depth(uint32 depth) 50 { 51 switch (depth) { 52 case 4: 53 return B_GRAY8; 54 // the app_server is smart enough to translate this to VGA mode 55 case 8: 56 return B_CMAP8; 57 case 15: 58 return B_RGB15; 59 case 16: 60 return B_RGB16; 61 case 24: 62 return B_RGB24; 63 case 32: 64 return B_RGB32; 65 } 66 67 return 0; 68 } 69 70 71 /*! Checks if the specified \a mode can be set using VESA. */ 72 static bool 73 is_mode_supported(display_mode* mode) 74 { 75 vesa_mode* modes = gInfo->vesa_modes; 76 77 bool colorspaceSupported = false; 78 79 for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) { 80 // search mode in VESA mode list 81 // TODO: list is ordered, we could use binary search 82 if (modes[i].width == mode->virtual_width 83 && modes[i].height == mode->virtual_height 84 && get_color_space_for_depth(modes[i].bits_per_pixel) 85 == mode->space) 86 return true; 87 88 if (get_color_space_for_depth(modes[i].bits_per_pixel) == mode->space) 89 colorspaceSupported = true; 90 } 91 92 bios_type_enum type = gInfo->shared_info->bios_type; 93 if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) { 94 // We know how to patch the BIOS, so we can set any mode we want 95 return colorspaceSupported; 96 } 97 98 if (type == kNVidiaBiosType) { 99 for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) { 100 if (mode->virtual_width == kNVidiaAllowedResolutions[i].width 101 && mode->virtual_height == kNVidiaAllowedResolutions[i].height) 102 return colorspaceSupported; 103 } 104 } 105 106 return false; 107 } 108 109 110 /*! Creates the initial mode list of the primary accelerant. 111 It's called from vesa_init_accelerant(). 112 */ 113 status_t 114 create_mode_list(void) 115 { 116 const color_space kVesaSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE, 117 B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8}; 118 119 uint32 initialModesCount = 0; 120 121 // Add initial VESA modes. 122 display_mode* initialModes = (display_mode*)malloc( 123 sizeof(display_mode) * gInfo->shared_info->vesa_mode_count); 124 if (initialModes != NULL) { 125 initialModesCount = gInfo->shared_info->vesa_mode_count; 126 vesa_mode* vesaModes = gInfo->vesa_modes; 127 128 for (uint32 i = 0; i < initialModesCount; i++) { 129 compute_display_timing(vesaModes[i].width, vesaModes[i].height, 130 60, false, &initialModes[i].timing); 131 fill_display_mode(vesaModes[i].width, vesaModes[i].height, 132 &initialModes[i]); 133 } 134 } 135 136 gInfo->mode_list_area = create_display_modes("vesa modes", 137 gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL, 138 initialModes, initialModesCount, 139 kVesaSpaces, sizeof(kVesaSpaces) / sizeof(kVesaSpaces[0]), 140 is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count); 141 142 free(initialModes); 143 144 if (gInfo->mode_list_area < 0) 145 return gInfo->mode_list_area; 146 147 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 148 return B_OK; 149 } 150 151 152 // #pragma mark - 153 154 155 uint32 156 vesa_accelerant_mode_count(void) 157 { 158 TRACE(("vesa_accelerant_mode_count() = %d\n", gInfo->shared_info->mode_count)); 159 return gInfo->shared_info->mode_count; 160 } 161 162 163 status_t 164 vesa_get_mode_list(display_mode* modeList) 165 { 166 TRACE(("vesa_get_mode_info()\n")); 167 memcpy(modeList, gInfo->mode_list, 168 gInfo->shared_info->mode_count * sizeof(display_mode)); 169 return B_OK; 170 } 171 172 173 status_t 174 vesa_propose_display_mode(display_mode* target, const display_mode* low, 175 const display_mode* high) 176 { 177 TRACE(("vesa_propose_display_mode()\n")); 178 179 // Search for the specified mode in the list. If it's in there, we don't need a custom mode and 180 // we just normalize it to the info provided by the VESA BIOS. 181 182 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { 183 display_mode* current = &gInfo->mode_list[i]; 184 185 if (target->virtual_width != current->virtual_width 186 || target->virtual_height != current->virtual_height 187 || target->space != current->space) 188 continue; 189 190 *target = *current; 191 return B_OK; 192 } 193 194 bios_type_enum type = gInfo->shared_info->bios_type; 195 if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) { 196 // The driver says it knows the BIOS type, and therefore how to patch it to apply custom 197 // modes. 198 return B_OK; 199 } 200 201 if (type == kNVidiaBiosType) { 202 // For NVidia there is only a limited set of extra resolutions we know how to set 203 for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) { 204 if (target->virtual_width == kNVidiaAllowedResolutions[i].width 205 && target->virtual_height == kNVidiaAllowedResolutions[i].height) 206 return B_OK; 207 } 208 } 209 210 return B_BAD_VALUE; 211 } 212 213 214 status_t 215 vesa_set_display_mode(display_mode* _mode) 216 { 217 TRACE(("vesa_set_display_mode()\n")); 218 219 display_mode mode = *_mode; 220 if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK) 221 return B_BAD_VALUE; 222 223 vesa_mode* modes = gInfo->vesa_modes; 224 for (int32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) { 225 // search mode in VESA mode list 226 // TODO: list is ordered, we could use binary search 227 if (modes[i].width == mode.virtual_width 228 && modes[i].height == mode.virtual_height 229 && get_color_space_for_depth(modes[i].bits_per_pixel) 230 == mode.space) { 231 if (gInfo->current_mode == i) 232 return B_OK; 233 status_t result = ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i)); 234 if (result == B_OK) 235 gInfo->current_mode = i; 236 return result; 237 } 238 } 239 240 // If the mode is not found in the list of standard mode, live patch the BIOS to get it anyway 241 status_t result = ioctl(gInfo->device, VESA_SET_CUSTOM_DISPLAY_MODE, 242 &mode, sizeof(display_mode)); 243 if (result == B_OK) { 244 gInfo->current_mode = -1; 245 } 246 247 return result; 248 } 249 250 251 status_t 252 vesa_get_display_mode(display_mode* _currentMode) 253 { 254 TRACE(("vesa_get_display_mode()\n")); 255 *_currentMode = gInfo->shared_info->current_mode; 256 return B_OK; 257 } 258 259 260 status_t 261 vesa_get_edid_info(void* info, size_t size, uint32* _version) 262 { 263 TRACE(("vesa_get_edid_info()\n")); 264 265 if (!gInfo->shared_info->has_edid) 266 return B_ERROR; 267 if (size < sizeof(struct edid1_info)) 268 return B_BUFFER_OVERFLOW; 269 270 memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info)); 271 *_version = EDID_VERSION_1; 272 return B_OK; 273 } 274 275 276 status_t 277 vesa_get_frame_buffer_config(frame_buffer_config* config) 278 { 279 TRACE(("vesa_get_frame_buffer_config()\n")); 280 281 config->frame_buffer = gInfo->shared_info->frame_buffer; 282 config->frame_buffer_dma = gInfo->shared_info->physical_frame_buffer; 283 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 284 285 return B_OK; 286 } 287 288 289 status_t 290 vesa_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 291 { 292 TRACE(("vesa_get_pixel_clock_limits()\n")); 293 294 // TODO: do some real stuff here (taken from radeon driver) 295 uint32 totalPixel = (uint32)mode->timing.h_total 296 * (uint32)mode->timing.v_total; 297 uint32 clockLimit = 2000000; 298 299 // lower limit of about 48Hz vertical refresh 300 *_low = totalPixel * 48L / 1000L; 301 if (*_low > clockLimit) 302 return B_ERROR; 303 304 *_high = clockLimit; 305 return B_OK; 306 } 307 308 309 status_t 310 vesa_move_display(uint16 h_display_start, uint16 v_display_start) 311 { 312 TRACE(("vesa_move_display()\n")); 313 return B_ERROR; 314 } 315 316 317 status_t 318 vesa_get_timing_constraints(display_timing_constraints* constraints) 319 { 320 TRACE(("vesa_get_timing_constraints()\n")); 321 return B_ERROR; 322 } 323 324 325 void 326 vesa_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 327 { 328 TRACE(("vesa_set_indexed_colors()\n")); 329 330 vesa_set_indexed_colors_args args; 331 args.first = first; 332 args.count = count; 333 args.colors = colors; 334 ioctl(gInfo->device, VESA_SET_INDEXED_COLORS, &args, sizeof(args)); 335 } 336 337