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