[XBOXVMP] Add Xbox video miniport driver
[reactos.git] / win32ss / drivers / miniport / xboxvmp / xboxvmp.c
1 /*
2 * ReactOS Xbox miniport video driver
3 * Copyright (C) 2004 Gé van Geldorp
4 *
5 * Based on VBE miniport video driver
6 * Copyright (C) 2004 Filip Navara
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * TODO:
23 * - Check input parameters everywhere.
24 * - Call VideoPortVerifyAccessRanges to reserve the memory we're about
25 * to map.
26 */
27
28 /* INCLUDES *******************************************************************/
29
30 #include "xboxvmp.h"
31
32 #define I2C_IO_BASE 0xc000
33
34 #define CONTROL_FRAMEBUFFER_ADDRESS_OFFSET 0x600800
35
36 /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/
37
38 ULONG NTAPI
39 DriverEntry(IN PVOID Context1, IN PVOID Context2)
40 {
41 VIDEO_HW_INITIALIZATION_DATA InitData;
42
43 VideoPortZeroMemory(&InitData, sizeof(InitData));
44 InitData.AdapterInterfaceType = PCIBus;
45 InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
46 InitData.HwFindAdapter = XboxVmpFindAdapter;
47 InitData.HwInitialize = XboxVmpInitialize;
48 InitData.HwStartIO = XboxVmpStartIO;
49 InitData.HwResetHw = XboxVmpResetHw;
50 InitData.HwGetPowerState = XboxVmpGetPowerState;
51 InitData.HwSetPowerState = XboxVmpSetPowerState;
52 InitData.HwDeviceExtensionSize = sizeof(XBOXVMP_DEVICE_EXTENSION);
53
54 return VideoPortInitialize(Context1, Context2, &InitData, NULL);
55 }
56
57 /*
58 * XboxVmpFindAdapter
59 *
60 * Detects the Xbox Nvidia display adapter.
61 */
62
63 VP_STATUS NTAPI
64 XboxVmpFindAdapter(
65 IN PVOID HwDeviceExtension,
66 IN PVOID HwContext,
67 IN PWSTR ArgumentString,
68 IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo,
69 OUT PUCHAR Again)
70 {
71 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension;
72 VIDEO_ACCESS_RANGE AccessRanges[3];
73 VP_STATUS Status;
74 USHORT VendorId = 0x10DE; /* NVIDIA Corporation */
75 USHORT DeviceId = 0x02A0; /* NV2A XGPU */
76
77 VideoPortDebugPrint(Trace, "XboxVmpFindAdapter\n");
78
79 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION) HwDeviceExtension;
80 Status = VideoPortGetAccessRanges(HwDeviceExtension, 0, NULL, 3, AccessRanges,
81 &VendorId, &DeviceId, NULL);
82
83 if (Status == NO_ERROR)
84 {
85 XboxVmpDeviceExtension->PhysControlStart = AccessRanges[0].RangeStart;
86 XboxVmpDeviceExtension->ControlLength = AccessRanges[0].RangeLength;
87 XboxVmpDeviceExtension->PhysFrameBufferStart = AccessRanges[1].RangeStart;
88 }
89
90 return Status;
91 }
92
93 /*
94 * XboxVmpInitialize
95 *
96 * Performs the first initialization of the adapter, after the HAL has given
97 * up control of the video hardware to the video port driver.
98 */
99
100 BOOLEAN NTAPI
101 XboxVmpInitialize(PVOID HwDeviceExtension)
102 {
103 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension;
104 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
105 ULONG Length;
106
107 VideoPortDebugPrint(Trace, "XboxVmpInitialize\n");
108
109 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION) HwDeviceExtension;
110
111 Length = XboxVmpDeviceExtension->ControlLength;
112 XboxVmpDeviceExtension->VirtControlStart = NULL;
113 if (NO_ERROR != VideoPortMapMemory(HwDeviceExtension,
114 XboxVmpDeviceExtension->PhysControlStart,
115 &Length, &inIoSpace,
116 &XboxVmpDeviceExtension->VirtControlStart))
117 {
118 VideoPortDebugPrint(Error, "Failed to map control memory\n");
119 return FALSE;
120 }
121 VideoPortDebugPrint(Info, "Mapped 0x%x bytes of control mem at 0x%x to virt addr 0x%x\n",
122 XboxVmpDeviceExtension->ControlLength,
123 XboxVmpDeviceExtension->PhysControlStart.u.LowPart,
124 XboxVmpDeviceExtension->VirtControlStart);
125
126 return TRUE;
127 }
128
129 /*
130 * XboxVmpStartIO
131 *
132 * Processes the specified Video Request Packet.
133 */
134
135 BOOLEAN NTAPI
136 XboxVmpStartIO(
137 PVOID HwDeviceExtension,
138 PVIDEO_REQUEST_PACKET RequestPacket)
139 {
140 BOOLEAN Result;
141
142 RequestPacket->StatusBlock->Status = ERROR_INVALID_PARAMETER;
143
144 switch (RequestPacket->IoControlCode)
145 {
146 case IOCTL_VIDEO_SET_CURRENT_MODE:
147 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_SET_CURRENT_MODE\n");
148 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
149 {
150 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
151 return TRUE;
152 }
153 Result = XboxVmpSetCurrentMode(
154 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
155 (PVIDEO_MODE)RequestPacket->InputBuffer,
156 RequestPacket->StatusBlock);
157 break;
158
159 case IOCTL_VIDEO_RESET_DEVICE:
160 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_RESET_DEVICE\n");
161 Result = XboxVmpResetDevice(
162 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
163 RequestPacket->StatusBlock);
164 break;
165
166 case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
167 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_MAP_VIDEO_MEMORY\n");
168 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) ||
169 RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
170 {
171 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
172 return TRUE;
173 }
174 Result = XboxVmpMapVideoMemory(
175 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
176 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
177 (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
178 RequestPacket->StatusBlock);
179 break;
180
181 case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
182 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_UNMAP_VIDEO_MEMORY\n");
183 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
184 {
185 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
186 return TRUE;
187 }
188 Result = XboxVmpUnmapVideoMemory(
189 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
190 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
191 RequestPacket->StatusBlock);
192 break;
193
194 case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
195 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES\n");
196 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
197 {
198 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
199 return TRUE;
200 }
201 Result = XboxVmpQueryNumAvailModes(
202 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
203 (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
204 RequestPacket->StatusBlock);
205 break;
206
207 case IOCTL_VIDEO_QUERY_AVAIL_MODES:
208 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_AVAIL_MODES\n");
209 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
210 {
211 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
212 return TRUE;
213 }
214 Result = XboxVmpQueryAvailModes(
215 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
216 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
217 RequestPacket->StatusBlock);
218 break;
219
220 case IOCTL_VIDEO_QUERY_CURRENT_MODE:
221 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_CURRENT_MODE\n");
222 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
223 {
224 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
225 return TRUE;
226 }
227 Result = XboxVmpQueryCurrentMode(
228 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
229 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
230 RequestPacket->StatusBlock);
231 break;
232
233 default:
234 VideoPortDebugPrint(Warn, "XboxVmpStartIO 0x%x not implemented\n");
235 RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
236 return FALSE;
237 }
238
239 if (Result)
240 {
241 RequestPacket->StatusBlock->Status = NO_ERROR;
242 }
243
244 return TRUE;
245 }
246
247 /*
248 * XboxVmpResetHw
249 *
250 * This function is called to reset the hardware to a known state.
251 */
252
253 BOOLEAN NTAPI
254 XboxVmpResetHw(
255 PVOID DeviceExtension,
256 ULONG Columns,
257 ULONG Rows)
258 {
259 VideoPortDebugPrint(Trace, "XboxVmpResetHw\n");
260
261 if (! XboxVmpResetDevice((PXBOXVMP_DEVICE_EXTENSION) DeviceExtension, NULL))
262 {
263 return FALSE;
264 }
265
266 return TRUE;
267 }
268
269 /*
270 * XboxVmpGetPowerState
271 *
272 * Queries whether the device can support the requested power state.
273 */
274
275 VP_STATUS NTAPI
276 XboxVmpGetPowerState(
277 PVOID HwDeviceExtension,
278 ULONG HwId,
279 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
280 {
281 VideoPortDebugPrint(Error, "XboxVmpGetPowerState is not supported\n");
282
283 return ERROR_INVALID_FUNCTION;
284 }
285
286 /*
287 * XboxVmpSetPowerState
288 *
289 * Sets the power state of the specified device
290 */
291
292 VP_STATUS NTAPI
293 XboxVmpSetPowerState(
294 PVOID HwDeviceExtension,
295 ULONG HwId,
296 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
297 {
298 VideoPortDebugPrint(Error, "XboxVmpSetPowerState not supported\n");
299
300 return ERROR_INVALID_FUNCTION;
301 }
302
303 /*
304 * VBESetCurrentMode
305 *
306 * Sets the adapter to the specified operating mode.
307 */
308
309 BOOLEAN FASTCALL
310 XboxVmpSetCurrentMode(
311 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
312 PVIDEO_MODE RequestedMode,
313 PSTATUS_BLOCK StatusBlock)
314 {
315 if (0 != RequestedMode->RequestedMode)
316 {
317 return FALSE;
318 }
319
320 /* Nothing to do, really. We only support a single mode and we're already
321 in that mode */
322 return TRUE;
323 }
324
325 /*
326 * XboxVmpResetDevice
327 *
328 * Resets the video hardware to the default mode, to which it was initialized
329 * at system boot.
330 */
331
332 BOOLEAN FASTCALL
333 XboxVmpResetDevice(
334 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
335 PSTATUS_BLOCK StatusBlock)
336 {
337 /* There is nothing to be done here */
338
339 return TRUE;
340 }
341
342 /*
343 * XboxVmpMapVideoMemory
344 *
345 * Maps the video hardware frame buffer and video RAM into the virtual address
346 * space of the requestor.
347 */
348
349 BOOLEAN FASTCALL
350 XboxVmpMapVideoMemory(
351 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
352 PVIDEO_MEMORY RequestedAddress,
353 PVIDEO_MEMORY_INFORMATION MapInformation,
354 PSTATUS_BLOCK StatusBlock)
355 {
356 PHYSICAL_ADDRESS FrameBuffer;
357 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
358 SYSTEM_BASIC_INFORMATION BasicInfo;
359 ULONG Length;
360
361 /* FIXME: this should probably be done differently, without native API */
362 StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
363
364 FrameBuffer.u.HighPart = 0;
365 if (ZwQuerySystemInformation(SystemBasicInformation,
366 (PVOID) &BasicInfo,
367 sizeof(SYSTEM_BASIC_INFORMATION),
368 &Length) == NO_ERROR)
369 {
370 FrameBuffer.u.LowPart = BasicInfo.HighestPhysicalPageNumber * PAGE_SIZE;
371 }
372 else
373 {
374 VideoPortDebugPrint(Error, "ZwQueryBasicInformation failed, assuming 64MB total memory\n");
375 FrameBuffer.u.LowPart = 60 * 1024 * 1024;
376 }
377
378 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart;
379 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
380 MapInformation->VideoRamLength = 4 * 1024 * 1024;
381 VideoPortMapMemory(DeviceExtension, FrameBuffer,
382 &MapInformation->VideoRamLength, &inIoSpace,
383 &MapInformation->VideoRamBase);
384
385 MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
386 MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
387
388 /* Tell the nVidia controller about the framebuffer */
389 *((PULONG)((char *) DeviceExtension->VirtControlStart + CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart;
390
391 VideoPortDebugPrint(Info, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n",
392 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase);
393
394 return TRUE;
395 }
396
397 /*
398 * VBEUnmapVideoMemory
399 *
400 * Releases a mapping between the virtual address space and the adapter's
401 * frame buffer and video RAM.
402 */
403
404 BOOLEAN FASTCALL
405 XboxVmpUnmapVideoMemory(
406 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
407 PVIDEO_MEMORY VideoMemory,
408 PSTATUS_BLOCK StatusBlock)
409 {
410 VideoPortUnmapMemory(DeviceExtension, VideoMemory->RequestedVirtualAddress,
411 NULL);
412
413 return TRUE;
414 }
415
416 /*
417 * XboxVmpQueryNumAvailModes
418 *
419 * Returns the number of video modes supported by the adapter and the size
420 * in bytes of the video mode information, which can be used to allocate a
421 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
422 */
423
424 BOOLEAN FASTCALL
425 XboxVmpQueryNumAvailModes(
426 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
427 PVIDEO_NUM_MODES Modes,
428 PSTATUS_BLOCK StatusBlock)
429 {
430 Modes->NumModes = 1;
431 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
432 StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
433 return TRUE;
434 }
435
436 static BOOLEAN
437 ReadfromSMBus(UCHAR Address, UCHAR bRegister, UCHAR Size, ULONG *Data_to_smbus)
438 {
439 int nRetriesToLive=50;
440
441 while (0 != (VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0)) & 0x0800))
442 {
443 ; /* Franz's spin while bus busy with any master traffic */
444 }
445
446 while (0 != nRetriesToLive--)
447 {
448 UCHAR b;
449 int temp;
450
451 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 4), (Address << 1) | 1);
452 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 8), bRegister);
453
454 temp = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0));
455 VideoPortWritePortUshort((PUSHORT) (I2C_IO_BASE + 0), temp); /* clear down all preexisting errors */
456
457 switch (Size)
458 {
459 case 4:
460 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0d); /* DWORD modus ? */
461 break;
462 case 2:
463 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0b); /* WORD modus */
464 break;
465 default:
466 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0a); // BYTE
467 break;
468 }
469
470 b = 0;
471
472 while (0 == (b & 0x36))
473 {
474 b = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 0));
475 }
476
477 if (0 != (b & 0x24))
478 {
479 /* printf("I2CTransmitByteGetReturn error %x\n", b); */
480 }
481
482 if(0 == (b & 0x10))
483 {
484 /* printf("I2CTransmitByteGetReturn no complete, retry\n"); */
485 }
486 else
487 {
488 switch (Size)
489 {
490 case 4:
491 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
492 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
493 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
494 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
495 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
496 break;
497 case 2:
498 *Data_to_smbus = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 6));
499 break;
500 default:
501 *Data_to_smbus = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
502 break;
503 }
504
505
506 return TRUE;
507 }
508 }
509
510 return FALSE;
511 }
512
513
514 static BOOLEAN
515 I2CTransmitByteGetReturn(UCHAR bPicAddressI2cFormat, UCHAR bDataToWrite, ULONG *Return)
516 {
517 return ReadfromSMBus(bPicAddressI2cFormat, bDataToWrite, 1, Return);
518 }
519
520 /*
521 * XboxVmpQueryAvailModes
522 *
523 * Returns information about each video mode supported by the adapter.
524 */
525
526 BOOLEAN FASTCALL
527 XboxVmpQueryAvailModes(
528 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
529 PVIDEO_MODE_INFORMATION VideoMode,
530 PSTATUS_BLOCK StatusBlock)
531 {
532 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock);
533 }
534
535 /*
536 * VBEQueryCurrentMode
537 *
538 * Returns information about current video mode.
539 */
540
541 BOOLEAN FASTCALL
542 XboxVmpQueryCurrentMode(
543 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
544 PVIDEO_MODE_INFORMATION VideoMode,
545 PSTATUS_BLOCK StatusBlock)
546 {
547 ULONG AvMode = 0;
548
549 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
550 VideoMode->ModeIndex = 0;
551 if (I2CTransmitByteGetReturn(0x10, 0x04, &AvMode))
552 {
553 if (1 == AvMode) /* HDTV */
554 {
555 VideoMode->VisScreenWidth = 720;
556 }
557 else
558 {
559 /* FIXME Other possible values of AvMode:
560 * 0 - AV_SCART_RGB
561 * 2 - AV_VGA_SOG
562 * 4 - AV_SVIDEO
563 * 6 - AV_COMPOSITE
564 * 7 - AV_VGA
565 * other AV_COMPOSITE
566 */
567 VideoMode->VisScreenWidth = 640;
568 }
569 }
570 else
571 {
572 VideoMode->VisScreenWidth = 640;
573 }
574 VideoMode->VisScreenHeight = 480;
575 VideoMode->ScreenStride = VideoMode->VisScreenWidth * 4;
576 VideoMode->NumberOfPlanes = 1;
577 VideoMode->BitsPerPlane = 32;
578 VideoMode->Frequency = 1;
579 VideoMode->XMillimeter = 0; /* FIXME */
580 VideoMode->YMillimeter = 0; /* FIXME */
581 VideoMode->NumberRedBits = 8;
582 VideoMode->NumberGreenBits = 8;
583 VideoMode->NumberBlueBits = 8;
584 VideoMode->RedMask = 0xff0000;
585 VideoMode->GreenMask = 0x00ff00;
586 VideoMode->BlueMask = 0x0000ff;
587 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth;
588 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight;
589 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
590 VIDEO_MODE_NO_OFF_SCREEN;
591 VideoMode->DriverSpecificAttributeFlags = 0;
592
593 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
594
595 return TRUE;
596 }
597
598 /* EOF */