[FREELDR][XBOXVMP] Retrieve screen resolution directly from NV2A GPU (#1962)
[reactos.git] / win32ss / drivers / miniport / xboxvmp / xboxvmp.c
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 if (FrameBuffer.QuadPart != 0x3C00000 && FrameBuffer.QuadPart != 0x7C00000)
398 {
399 /* Check framebuffer address (high 4 MB of either 64 or 128 MB RAM) */
400 WARN_(IHVVIDEO, "Non-standard framebuffer address 0x%p\n", FrameBuffer.QuadPart);
401 }
402 /* Verify that framebuffer address is page-aligned */
403 ASSERT(FrameBuffer.QuadPart % PAGE_SIZE == 0);
404
405 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart;
406 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
407 /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */
408 MapInformation->VideoRamLength = NV2A_VIDEO_MEMORY_SIZE;
409
410 VideoPortMapMemory(
411 DeviceExtension,
412 FrameBuffer,
413 &MapInformation->VideoRamLength,
414 &inIoSpace,
415 &MapInformation->VideoRamBase);
416
417 MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
418 MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
419
420 /* Tell the nVidia controller about the framebuffer */
421 *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart;
422
423 INFO_(IHVVIDEO, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n",
424 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase);
425
426 return TRUE;
427 }
428
429 /*
430 * VBEUnmapVideoMemory
431 *
432 * Releases a mapping between the virtual address space and the adapter's
433 * frame buffer and video RAM.
434 */
435
436 BOOLEAN
437 FASTCALL
438 XboxVmpUnmapVideoMemory(
439 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
440 PVIDEO_MEMORY VideoMemory,
441 PSTATUS_BLOCK StatusBlock)
442 {
443 VideoPortUnmapMemory(
444 DeviceExtension,
445 VideoMemory->RequestedVirtualAddress,
446 NULL);
447
448 return TRUE;
449 }
450
451 /*
452 * XboxVmpQueryNumAvailModes
453 *
454 * Returns the number of video modes supported by the adapter and the size
455 * in bytes of the video mode information, which can be used to allocate a
456 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
457 */
458
459 BOOLEAN
460 FASTCALL
461 XboxVmpQueryNumAvailModes(
462 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
463 PVIDEO_NUM_MODES Modes,
464 PSTATUS_BLOCK StatusBlock)
465 {
466 Modes->NumModes = 1;
467 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
468 StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
469 return TRUE;
470 }
471
472 /*
473 * XboxVmpQueryAvailModes
474 *
475 * Returns information about each video mode supported by the adapter.
476 */
477
478 BOOLEAN
479 FASTCALL
480 XboxVmpQueryAvailModes(
481 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
482 PVIDEO_MODE_INFORMATION VideoMode,
483 PSTATUS_BLOCK StatusBlock)
484 {
485 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock);
486 }
487
488 UCHAR
489 NvGetCrtc(
490 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
491 UCHAR Index)
492 {
493 *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_INDEX)) = Index;
494 return *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_VALUE));
495 }
496
497 UCHAR
498 NvGetBytesPerPixel(
499 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
500 ULONG ScreenWidth)
501 {
502 UCHAR BytesPerPixel;
503
504 /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */
505 BytesPerPixel = 8 * (((NvGetCrtc(DeviceExtension, 0x19) & 0xE0) << 3) | (NvGetCrtc(DeviceExtension, 0x13) & 0xFF)) / ScreenWidth;
506
507 if (BytesPerPixel == 4)
508 {
509 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel - 1);
510 }
511 else
512 {
513 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel);
514 }
515
516 return BytesPerPixel;
517 }
518
519 /*
520 * VBEQueryCurrentMode
521 *
522 * Returns information about current video mode.
523 */
524
525 BOOLEAN
526 FASTCALL
527 XboxVmpQueryCurrentMode(
528 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
529 PVIDEO_MODE_INFORMATION VideoMode,
530 PSTATUS_BLOCK StatusBlock)
531 {
532 UCHAR BytesPerPixel;
533
534 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
535 VideoMode->ModeIndex = 0;
536
537 VideoMode->VisScreenWidth = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_HVALID_END)) + 1;
538 VideoMode->VisScreenHeight = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_VVALID_END)) + 1;
539
540 if (VideoMode->VisScreenWidth <= 1 || VideoMode->VisScreenHeight <= 1)
541 {
542 ERR_(IHVVIDEO, "Cannot obtain current screen resolution!\n");
543 return FALSE;
544 }
545
546 BytesPerPixel = NvGetBytesPerPixel(DeviceExtension, VideoMode->VisScreenWidth);
547 ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4);
548
549 VideoMode->ScreenStride = VideoMode->VisScreenWidth * BytesPerPixel;
550 VideoMode->NumberOfPlanes = 1;
551 VideoMode->BitsPerPlane = BytesPerPixel * 8;
552 VideoMode->Frequency = 1;
553 VideoMode->XMillimeter = 0; /* FIXME */
554 VideoMode->YMillimeter = 0; /* FIXME */
555 if (BytesPerPixel >= 3)
556 {
557 VideoMode->NumberRedBits = 8;
558 VideoMode->NumberGreenBits = 8;
559 VideoMode->NumberBlueBits = 8;
560 VideoMode->RedMask = 0xFF0000;
561 VideoMode->GreenMask = 0x00FF00;
562 VideoMode->BlueMask = 0x0000FF;
563 }
564 else
565 {
566 /* FIXME: not implemented */
567 WARN_(IHVVIDEO, "BytesPerPixel %d - not implemented\n", BytesPerPixel);
568 }
569 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth;
570 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight;
571 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
572 VIDEO_MODE_NO_OFF_SCREEN;
573 VideoMode->DriverSpecificAttributeFlags = 0;
574
575 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
576
577 /* Verify that screen fits framebuffer size */
578 if (VideoMode->VisScreenWidth * VideoMode->VisScreenHeight * (VideoMode->BitsPerPlane / 8) > NV2A_VIDEO_MEMORY_SIZE)
579 {
580 ERR_(IHVVIDEO, "Current screen resolution exceeds video memory bounds!\n");
581 return FALSE;
582 }
583
584 return TRUE;
585 }
586
587 /* EOF */