ee6c7fd2e25b2cf6e3d44793d1741cfba9705e28
[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 SYSTEM_BASIC_INFORMATION BasicInfo;
393 ULONG Length;
394
395 /* FIXME: this should probably be done differently, without native API */
396 StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
397
398 FrameBuffer.u.HighPart = 0;
399 if (ZwQuerySystemInformation(SystemBasicInformation,
400 (PVOID)&BasicInfo,
401 sizeof(SYSTEM_BASIC_INFORMATION),
402 &Length) == NO_ERROR)
403 {
404 FrameBuffer.u.LowPart = BasicInfo.HighestPhysicalPageNumber * PAGE_SIZE;
405 }
406 else
407 {
408 ERR_(IHVVIDEO, "ZwQueryBasicInformation failed, assuming 64MB total memory\n");
409 FrameBuffer.u.LowPart = 60 * 1024 * 1024;
410 }
411
412 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart;
413 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
414 MapInformation->VideoRamLength = 4 * 1024 * 1024;
415
416 VideoPortMapMemory(
417 DeviceExtension,
418 FrameBuffer,
419 &MapInformation->VideoRamLength,
420 &inIoSpace,
421 &MapInformation->VideoRamBase);
422
423 MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
424 MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
425
426 /* Tell the nVidia controller about the framebuffer */
427 *((PULONG)((char *)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart;
428
429 INFO_(IHVVIDEO, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n",
430 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase);
431
432 return TRUE;
433 }
434
435 /*
436 * VBEUnmapVideoMemory
437 *
438 * Releases a mapping between the virtual address space and the adapter's
439 * frame buffer and video RAM.
440 */
441
442 BOOLEAN
443 FASTCALL
444 XboxVmpUnmapVideoMemory(
445 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
446 PVIDEO_MEMORY VideoMemory,
447 PSTATUS_BLOCK StatusBlock)
448 {
449 VideoPortUnmapMemory(
450 DeviceExtension,
451 VideoMemory->RequestedVirtualAddress,
452 NULL);
453
454 return TRUE;
455 }
456
457 /*
458 * XboxVmpQueryNumAvailModes
459 *
460 * Returns the number of video modes supported by the adapter and the size
461 * in bytes of the video mode information, which can be used to allocate a
462 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
463 */
464
465 BOOLEAN
466 FASTCALL
467 XboxVmpQueryNumAvailModes(
468 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
469 PVIDEO_NUM_MODES Modes,
470 PSTATUS_BLOCK StatusBlock)
471 {
472 Modes->NumModes = 1;
473 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
474 StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
475 return TRUE;
476 }
477
478 static
479 BOOLEAN
480 ReadfromSMBus(
481 UCHAR Address,
482 UCHAR bRegister,
483 UCHAR Size,
484 ULONG *Data_to_smbus)
485 {
486 int nRetriesToLive = 50;
487
488 while ((VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0)) & 0x0800) != 0)
489 {
490 ; /* Franz's spin while bus busy with any master traffic */
491 }
492
493 while (nRetriesToLive-- != 0)
494 {
495 UCHAR b;
496 int temp;
497
498 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 4), (Address << 1) | 1);
499 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 8), bRegister);
500
501 temp = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0));
502 VideoPortWritePortUshort((PUSHORT) (I2C_IO_BASE + 0), temp); /* clear down all preexisting errors */
503
504 switch (Size)
505 {
506 case 4:
507 {
508 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0d); /* DWORD modus ? */
509 break;
510 }
511
512 case 2:
513 {
514 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0b); /* WORD modus */
515 break;
516 }
517
518 default:
519 {
520 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0a); /* BYTE */
521 }
522 }
523
524 b = 0;
525
526 while ((b & 0x36) == 0)
527 {
528 b = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 0));
529 }
530
531 if ((b & 0x24) != 0)
532 {
533 ERR_(IHVVIDEO, "I2CTransmitByteGetReturn error %x\n", b);
534 }
535
536 if ((b & 0x10) == 0)
537 {
538 ERR_(IHVVIDEO, "I2CTransmitByteGetReturn no complete, retry\n");
539 }
540 else
541 {
542 switch (Size)
543 {
544 case 4:
545 {
546 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
547 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
548 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
549 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
550 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
551 break;
552 }
553
554 case 2:
555 {
556 *Data_to_smbus = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 6));
557 break;
558 }
559
560 default:
561 {
562 *Data_to_smbus = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
563 }
564 }
565
566 return TRUE;
567 }
568 }
569
570 return FALSE;
571 }
572
573
574 static
575 BOOLEAN
576 I2CTransmitByteGetReturn(
577 UCHAR bPicAddressI2cFormat,
578 UCHAR bDataToWrite,
579 ULONG *Return)
580 {
581 return ReadfromSMBus(bPicAddressI2cFormat, bDataToWrite, 1, Return);
582 }
583
584 /*
585 * XboxVmpQueryAvailModes
586 *
587 * Returns information about each video mode supported by the adapter.
588 */
589
590 BOOLEAN
591 FASTCALL
592 XboxVmpQueryAvailModes(
593 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
594 PVIDEO_MODE_INFORMATION VideoMode,
595 PSTATUS_BLOCK StatusBlock)
596 {
597 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock);
598 }
599
600 /*
601 * VBEQueryCurrentMode
602 *
603 * Returns information about current video mode.
604 */
605
606 BOOLEAN
607 FASTCALL
608 XboxVmpQueryCurrentMode(
609 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
610 PVIDEO_MODE_INFORMATION VideoMode,
611 PSTATUS_BLOCK StatusBlock)
612 {
613 ULONG AvMode = 0;
614
615 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
616 VideoMode->ModeIndex = 0;
617
618 if (I2CTransmitByteGetReturn(0x10, 0x04, &AvMode))
619 {
620 if (AvMode == 1) /* HDTV */
621 {
622 VideoMode->VisScreenWidth = 720;
623 }
624 else
625 {
626 /* FIXME Other possible values of AvMode:
627 * 0 - AV_SCART_RGB
628 * 2 - AV_VGA_SOG
629 * 4 - AV_SVIDEO
630 * 6 - AV_COMPOSITE
631 * 7 - AV_VGA
632 * other AV_COMPOSITE
633 */
634 VideoMode->VisScreenWidth = 640;
635 }
636 }
637 else
638 {
639 VideoMode->VisScreenWidth = 640;
640 }
641
642 VideoMode->VisScreenHeight = 480;
643 VideoMode->ScreenStride = VideoMode->VisScreenWidth * 4;
644 VideoMode->NumberOfPlanes = 1;
645 VideoMode->BitsPerPlane = 32;
646 VideoMode->Frequency = 1;
647 VideoMode->XMillimeter = 0; /* FIXME */
648 VideoMode->YMillimeter = 0; /* FIXME */
649 VideoMode->NumberRedBits = 8;
650 VideoMode->NumberGreenBits = 8;
651 VideoMode->NumberBlueBits = 8;
652 VideoMode->RedMask = 0xFF0000;
653 VideoMode->GreenMask = 0x00FF00;
654 VideoMode->BlueMask = 0x0000FF;
655 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth;
656 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight;
657 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
658 VIDEO_MODE_NO_OFF_SCREEN;
659 VideoMode->DriverSpecificAttributeFlags = 0;
660
661 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
662
663 return TRUE;
664 }
665
666 /* EOF */