1 /* 2 * PROJECT: ReactOS Xbox miniport video driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Simple framebuffer driver for NVIDIA NV2A XGPU 5 * COPYRIGHT: Copyright 2004 Gé van Geldorp 6 * Copyright 2004 Filip Navara 7 * Copyright 2019 Stanislav Motylkov (x86corez@gmail.com) 8 * 9 * TODO: 10 * - Check input parameters everywhere. 11 * - Call VideoPortVerifyAccessRanges to reserve the memory we're about 12 * to map. 13 */ 14 15 /* INCLUDES *******************************************************************/ 16 17 #include "xboxvmp.h" 18 19 #include <debug.h> 20 #include <dpfilter.h> 21 22 /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/ 23 24 ULONG 25 NTAPI 26 DriverEntry( 27 IN PVOID Context1, 28 IN PVOID Context2) 29 { 30 VIDEO_HW_INITIALIZATION_DATA InitData; 31 32 VideoPortZeroMemory(&InitData, sizeof(InitData)); 33 InitData.AdapterInterfaceType = PCIBus; 34 InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA); 35 InitData.HwFindAdapter = XboxVmpFindAdapter; 36 InitData.HwInitialize = XboxVmpInitialize; 37 InitData.HwStartIO = XboxVmpStartIO; 38 InitData.HwResetHw = XboxVmpResetHw; 39 InitData.HwGetPowerState = XboxVmpGetPowerState; 40 InitData.HwSetPowerState = XboxVmpSetPowerState; 41 InitData.HwDeviceExtensionSize = sizeof(XBOXVMP_DEVICE_EXTENSION); 42 43 return VideoPortInitialize(Context1, Context2, &InitData, NULL); 44 } 45 46 /* 47 * XboxVmpFindAdapter 48 * 49 * Detects the Xbox Nvidia display adapter. 50 */ 51 52 VP_STATUS 53 NTAPI 54 XboxVmpFindAdapter( 55 IN PVOID HwDeviceExtension, 56 IN PVOID HwContext, 57 IN PWSTR ArgumentString, 58 IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo, 59 OUT PUCHAR Again) 60 { 61 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension; 62 VIDEO_ACCESS_RANGE AccessRanges[3]; 63 VP_STATUS Status; 64 USHORT VendorId = 0x10DE; /* NVIDIA Corporation */ 65 USHORT DeviceId = 0x02A0; /* NV2A XGPU */ 66 67 TRACE_(IHVVIDEO, "XboxVmpFindAdapter\n"); 68 69 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension; 70 71 Status = VideoPortGetAccessRanges(HwDeviceExtension, 0, NULL, 3, AccessRanges, 72 &VendorId, &DeviceId, NULL); 73 74 if (Status == NO_ERROR) 75 { 76 XboxVmpDeviceExtension->PhysControlStart = AccessRanges[0].RangeStart; 77 XboxVmpDeviceExtension->ControlLength = AccessRanges[0].RangeLength; 78 XboxVmpDeviceExtension->PhysFrameBufferStart = AccessRanges[1].RangeStart; 79 } 80 81 return Status; 82 } 83 84 /* 85 * XboxVmpInitialize 86 * 87 * Performs the first initialization of the adapter, after the HAL has given 88 * up control of the video hardware to the video port driver. 89 */ 90 91 BOOLEAN 92 NTAPI 93 XboxVmpInitialize( 94 PVOID HwDeviceExtension) 95 { 96 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension; 97 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY; 98 ULONG Length; 99 100 TRACE_(IHVVIDEO, "XboxVmpInitialize\n"); 101 102 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension; 103 104 Length = XboxVmpDeviceExtension->ControlLength; 105 XboxVmpDeviceExtension->VirtControlStart = NULL; 106 107 if (VideoPortMapMemory(HwDeviceExtension, 108 XboxVmpDeviceExtension->PhysControlStart, 109 &Length, 110 &inIoSpace, 111 &XboxVmpDeviceExtension->VirtControlStart) != NO_ERROR) 112 { 113 ERR_(IHVVIDEO, "Failed to map control memory\n"); 114 return FALSE; 115 } 116 117 INFO_(IHVVIDEO, "Mapped 0x%x bytes of control mem at 0x%x to virt addr 0x%x\n", 118 XboxVmpDeviceExtension->ControlLength, 119 XboxVmpDeviceExtension->PhysControlStart.u.LowPart, 120 XboxVmpDeviceExtension->VirtControlStart); 121 122 return TRUE; 123 } 124 125 /* 126 * XboxVmpStartIO 127 * 128 * Processes the specified Video Request Packet. 129 */ 130 131 BOOLEAN 132 NTAPI 133 XboxVmpStartIO( 134 PVOID HwDeviceExtension, 135 PVIDEO_REQUEST_PACKET RequestPacket) 136 { 137 BOOLEAN Result; 138 139 RequestPacket->StatusBlock->Status = ERROR_INVALID_PARAMETER; 140 141 switch (RequestPacket->IoControlCode) 142 { 143 case IOCTL_VIDEO_SET_CURRENT_MODE: 144 { 145 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_SET_CURRENT_MODE\n"); 146 147 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE)) 148 { 149 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 150 return TRUE; 151 } 152 153 Result = XboxVmpSetCurrentMode( 154 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 155 (PVIDEO_MODE)RequestPacket->InputBuffer, 156 RequestPacket->StatusBlock); 157 break; 158 } 159 160 case IOCTL_VIDEO_RESET_DEVICE: 161 { 162 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_RESET_DEVICE\n"); 163 164 Result = XboxVmpResetDevice( 165 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 166 RequestPacket->StatusBlock); 167 break; 168 } 169 170 case IOCTL_VIDEO_MAP_VIDEO_MEMORY: 171 { 172 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_MAP_VIDEO_MEMORY\n"); 173 174 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) || 175 RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY)) 176 { 177 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 178 return TRUE; 179 } 180 181 Result = XboxVmpMapVideoMemory( 182 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 183 (PVIDEO_MEMORY)RequestPacket->InputBuffer, 184 (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer, 185 RequestPacket->StatusBlock); 186 break; 187 } 188 189 case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY: 190 { 191 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_UNMAP_VIDEO_MEMORY\n"); 192 193 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY)) 194 { 195 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 196 return TRUE; 197 } 198 199 Result = XboxVmpUnmapVideoMemory( 200 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 201 (PVIDEO_MEMORY)RequestPacket->InputBuffer, 202 RequestPacket->StatusBlock); 203 break; 204 } 205 206 case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES: 207 { 208 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES\n"); 209 210 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES)) 211 { 212 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 213 return TRUE; 214 } 215 216 Result = XboxVmpQueryNumAvailModes( 217 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 218 (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer, 219 RequestPacket->StatusBlock); 220 break; 221 } 222 223 case IOCTL_VIDEO_QUERY_AVAIL_MODES: 224 { 225 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_AVAIL_MODES\n"); 226 227 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION)) 228 { 229 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 230 return TRUE; 231 } 232 233 Result = XboxVmpQueryAvailModes( 234 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 235 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer, 236 RequestPacket->StatusBlock); 237 break; 238 } 239 240 case IOCTL_VIDEO_QUERY_CURRENT_MODE: 241 { 242 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_CURRENT_MODE\n"); 243 244 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION)) 245 { 246 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER; 247 return TRUE; 248 } 249 250 Result = XboxVmpQueryCurrentMode( 251 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension, 252 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer, 253 RequestPacket->StatusBlock); 254 break; 255 } 256 257 default: 258 { 259 WARN_(IHVVIDEO, "XboxVmpStartIO 0x%x not implemented\n", RequestPacket->IoControlCode); 260 261 RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION; 262 return FALSE; 263 } 264 } 265 266 if (Result) 267 { 268 RequestPacket->StatusBlock->Status = NO_ERROR; 269 } 270 271 return TRUE; 272 } 273 274 /* 275 * XboxVmpResetHw 276 * 277 * This function is called to reset the hardware to a known state. 278 */ 279 280 BOOLEAN 281 NTAPI 282 XboxVmpResetHw( 283 PVOID DeviceExtension, 284 ULONG Columns, 285 ULONG Rows) 286 { 287 TRACE_(IHVVIDEO, "XboxVmpResetHw\n"); 288 289 if (!XboxVmpResetDevice((PXBOXVMP_DEVICE_EXTENSION)DeviceExtension, NULL)) 290 { 291 return FALSE; 292 } 293 294 return TRUE; 295 } 296 297 /* 298 * XboxVmpGetPowerState 299 * 300 * Queries whether the device can support the requested power state. 301 */ 302 303 VP_STATUS 304 NTAPI 305 XboxVmpGetPowerState( 306 PVOID HwDeviceExtension, 307 ULONG HwId, 308 PVIDEO_POWER_MANAGEMENT VideoPowerControl) 309 { 310 ERR_(IHVVIDEO, "XboxVmpGetPowerState is not supported\n"); 311 312 return ERROR_INVALID_FUNCTION; 313 } 314 315 /* 316 * XboxVmpSetPowerState 317 * 318 * Sets the power state of the specified device 319 */ 320 321 VP_STATUS 322 NTAPI 323 XboxVmpSetPowerState( 324 PVOID HwDeviceExtension, 325 ULONG HwId, 326 PVIDEO_POWER_MANAGEMENT VideoPowerControl) 327 { 328 ERR_(IHVVIDEO, "XboxVmpSetPowerState not supported\n"); 329 330 return ERROR_INVALID_FUNCTION; 331 } 332 333 /* 334 * VBESetCurrentMode 335 * 336 * Sets the adapter to the specified operating mode. 337 */ 338 339 BOOLEAN 340 FASTCALL 341 XboxVmpSetCurrentMode( 342 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 343 PVIDEO_MODE RequestedMode, 344 PSTATUS_BLOCK StatusBlock) 345 { 346 if (RequestedMode->RequestedMode != 0) 347 { 348 return FALSE; 349 } 350 351 /* Nothing to do, really. We only support a single mode and we're already 352 * in that mode 353 */ 354 return TRUE; 355 } 356 357 /* 358 * XboxVmpResetDevice 359 * 360 * Resets the video hardware to the default mode, to which it was initialized 361 * at system boot. 362 */ 363 364 BOOLEAN 365 FASTCALL 366 XboxVmpResetDevice( 367 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 368 PSTATUS_BLOCK StatusBlock) 369 { 370 /* There is nothing to be done here */ 371 372 return TRUE; 373 } 374 375 /* 376 * XboxVmpMapVideoMemory 377 * 378 * Maps the video hardware frame buffer and video RAM into the virtual address 379 * space of the requestor. 380 */ 381 382 BOOLEAN 383 FASTCALL 384 XboxVmpMapVideoMemory( 385 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 386 PVIDEO_MEMORY RequestedAddress, 387 PVIDEO_MEMORY_INFORMATION MapInformation, 388 PSTATUS_BLOCK StatusBlock) 389 { 390 PHYSICAL_ADDRESS FrameBuffer; 391 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY; 392 393 StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION); 394 395 /* Reuse framebuffer that was set up by firmware */ 396 FrameBuffer.QuadPart = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)); 397 /* Framebuffer address offset value is coming from the GPU within 398 * memory mapped I/O address space, so we're comparing only low 399 * 28 bits of the address within actual RAM address space */ 400 FrameBuffer.QuadPart &= 0x0FFFFFFF; 401 if (FrameBuffer.QuadPart != 0x3C00000 && FrameBuffer.QuadPart != 0x7C00000) 402 { 403 /* Check framebuffer address (high 4 MB of either 64 or 128 MB RAM) */ 404 WARN_(IHVVIDEO, "Non-standard framebuffer address 0x%p\n", FrameBuffer.QuadPart); 405 } 406 /* Verify that framebuffer address is page-aligned */ 407 ASSERT(FrameBuffer.QuadPart % PAGE_SIZE == 0); 408 409 /* Return the address back to GPU memory mapped I/O */ 410 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart; 411 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress; 412 /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */ 413 MapInformation->VideoRamLength = NV2A_VIDEO_MEMORY_SIZE; 414 415 VideoPortMapMemory( 416 DeviceExtension, 417 FrameBuffer, 418 &MapInformation->VideoRamLength, 419 &inIoSpace, 420 &MapInformation->VideoRamBase); 421 422 MapInformation->FrameBufferBase = MapInformation->VideoRamBase; 423 MapInformation->FrameBufferLength = MapInformation->VideoRamLength; 424 425 /* Tell the nVidia controller about the framebuffer */ 426 *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart; 427 428 INFO_(IHVVIDEO, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n", 429 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase); 430 431 return TRUE; 432 } 433 434 /* 435 * VBEUnmapVideoMemory 436 * 437 * Releases a mapping between the virtual address space and the adapter's 438 * frame buffer and video RAM. 439 */ 440 441 BOOLEAN 442 FASTCALL 443 XboxVmpUnmapVideoMemory( 444 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 445 PVIDEO_MEMORY VideoMemory, 446 PSTATUS_BLOCK StatusBlock) 447 { 448 VideoPortUnmapMemory( 449 DeviceExtension, 450 VideoMemory->RequestedVirtualAddress, 451 NULL); 452 453 return TRUE; 454 } 455 456 /* 457 * XboxVmpQueryNumAvailModes 458 * 459 * Returns the number of video modes supported by the adapter and the size 460 * in bytes of the video mode information, which can be used to allocate a 461 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request. 462 */ 463 464 BOOLEAN 465 FASTCALL 466 XboxVmpQueryNumAvailModes( 467 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 468 PVIDEO_NUM_MODES Modes, 469 PSTATUS_BLOCK StatusBlock) 470 { 471 Modes->NumModes = 1; 472 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION); 473 StatusBlock->Information = sizeof(VIDEO_NUM_MODES); 474 return TRUE; 475 } 476 477 /* 478 * XboxVmpQueryAvailModes 479 * 480 * Returns information about each video mode supported by the adapter. 481 */ 482 483 BOOLEAN 484 FASTCALL 485 XboxVmpQueryAvailModes( 486 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 487 PVIDEO_MODE_INFORMATION VideoMode, 488 PSTATUS_BLOCK StatusBlock) 489 { 490 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock); 491 } 492 493 UCHAR 494 NvGetCrtc( 495 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 496 UCHAR Index) 497 { 498 *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_INDEX)) = Index; 499 return *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_VALUE)); 500 } 501 502 UCHAR 503 NvGetBytesPerPixel( 504 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 505 ULONG ScreenWidth) 506 { 507 UCHAR BytesPerPixel; 508 509 /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */ 510 BytesPerPixel = 8 * (((NvGetCrtc(DeviceExtension, 0x19) & 0xE0) << 3) | (NvGetCrtc(DeviceExtension, 0x13) & 0xFF)) / ScreenWidth; 511 512 if (BytesPerPixel == 4) 513 { 514 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel - 1); 515 } 516 else 517 { 518 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel); 519 } 520 521 return BytesPerPixel; 522 } 523 524 /* 525 * VBEQueryCurrentMode 526 * 527 * Returns information about current video mode. 528 */ 529 530 BOOLEAN 531 FASTCALL 532 XboxVmpQueryCurrentMode( 533 PXBOXVMP_DEVICE_EXTENSION DeviceExtension, 534 PVIDEO_MODE_INFORMATION VideoMode, 535 PSTATUS_BLOCK StatusBlock) 536 { 537 UCHAR BytesPerPixel; 538 539 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION); 540 VideoMode->ModeIndex = 0; 541 542 VideoMode->VisScreenWidth = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_HVALID_END)) + 1; 543 VideoMode->VisScreenHeight = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_VVALID_END)) + 1; 544 545 if (VideoMode->VisScreenWidth <= 1 || VideoMode->VisScreenHeight <= 1) 546 { 547 ERR_(IHVVIDEO, "Cannot obtain current screen resolution!\n"); 548 return FALSE; 549 } 550 551 BytesPerPixel = NvGetBytesPerPixel(DeviceExtension, VideoMode->VisScreenWidth); 552 ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4); 553 554 VideoMode->ScreenStride = VideoMode->VisScreenWidth * BytesPerPixel; 555 VideoMode->NumberOfPlanes = 1; 556 VideoMode->BitsPerPlane = BytesPerPixel * 8; 557 VideoMode->Frequency = 1; 558 VideoMode->XMillimeter = 0; /* FIXME */ 559 VideoMode->YMillimeter = 0; /* FIXME */ 560 if (BytesPerPixel >= 3) 561 { 562 VideoMode->NumberRedBits = 8; 563 VideoMode->NumberGreenBits = 8; 564 VideoMode->NumberBlueBits = 8; 565 VideoMode->RedMask = 0xFF0000; 566 VideoMode->GreenMask = 0x00FF00; 567 VideoMode->BlueMask = 0x0000FF; 568 } 569 else 570 { 571 /* FIXME: not implemented */ 572 WARN_(IHVVIDEO, "BytesPerPixel %d - not implemented\n", BytesPerPixel); 573 } 574 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth; 575 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight; 576 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | 577 VIDEO_MODE_NO_OFF_SCREEN; 578 VideoMode->DriverSpecificAttributeFlags = 0; 579 580 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION); 581 582 /* Verify that screen fits framebuffer size */ 583 if (VideoMode->VisScreenWidth * VideoMode->VisScreenHeight * (VideoMode->BitsPerPlane / 8) > NV2A_VIDEO_MEMORY_SIZE) 584 { 585 ERR_(IHVVIDEO, "Current screen resolution exceeds video memory bounds!\n"); 586 return FALSE; 587 } 588 589 return TRUE; 590 } 591 592 /* EOF */ 593