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