* Sync up to trunk head (r64716).
[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
75 VideoPortDebugPrint(Trace, "XboxVmpFindAdapter\n");
76
77 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION) HwDeviceExtension;
78 Status = VideoPortGetAccessRanges(HwDeviceExtension, 0, NULL, 3, AccessRanges,
79 NULL, NULL, NULL);
80
81 if (NO_ERROR == Status)
82 {
83 XboxVmpDeviceExtension->PhysControlStart = AccessRanges[0].RangeStart;
84 XboxVmpDeviceExtension->ControlLength = AccessRanges[0].RangeLength;
85 XboxVmpDeviceExtension->PhysFrameBufferStart = AccessRanges[1].RangeStart;
86 }
87
88 return Status;
89 }
90
91 /*
92 * XboxVmpInitialize
93 *
94 * Performs the first initialization of the adapter, after the HAL has given
95 * up control of the video hardware to the video port driver.
96 */
97
98 BOOLEAN NTAPI
99 XboxVmpInitialize(PVOID HwDeviceExtension)
100 {
101 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension;
102 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
103 ULONG Length;
104
105 VideoPortDebugPrint(Trace, "XboxVmpInitialize\n");
106
107 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION) HwDeviceExtension;
108
109 Length = XboxVmpDeviceExtension->ControlLength;
110 XboxVmpDeviceExtension->VirtControlStart = NULL;
111 if (NO_ERROR != VideoPortMapMemory(HwDeviceExtension,
112 XboxVmpDeviceExtension->PhysControlStart,
113 &Length, &inIoSpace,
114 &XboxVmpDeviceExtension->VirtControlStart))
115 {
116 VideoPortDebugPrint(Error, "Failed to map control memory\n");
117 return FALSE;
118 }
119 VideoPortDebugPrint(Info, "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 NTAPI
134 XboxVmpStartIO(
135 PVOID HwDeviceExtension,
136 PVIDEO_REQUEST_PACKET RequestPacket)
137 {
138 BOOLEAN Result;
139
140 RequestPacket->StatusBlock->Status = ERROR_INVALID_PARAMETER;
141
142 switch (RequestPacket->IoControlCode)
143 {
144 case IOCTL_VIDEO_SET_CURRENT_MODE:
145 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_SET_CURRENT_MODE\n");
146 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
147 {
148 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
149 return TRUE;
150 }
151 Result = XboxVmpSetCurrentMode(
152 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
153 (PVIDEO_MODE)RequestPacket->InputBuffer,
154 RequestPacket->StatusBlock);
155 break;
156
157 case IOCTL_VIDEO_RESET_DEVICE:
158 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_RESET_DEVICE\n");
159 Result = XboxVmpResetDevice(
160 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
161 RequestPacket->StatusBlock);
162 break;
163
164 case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
165 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_MAP_VIDEO_MEMORY\n");
166 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) ||
167 RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
168 {
169 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
170 return TRUE;
171 }
172 Result = XboxVmpMapVideoMemory(
173 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
174 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
175 (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
176 RequestPacket->StatusBlock);
177 break;
178
179 case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
180 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_UNMAP_VIDEO_MEMORY\n");
181 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
182 {
183 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
184 return TRUE;
185 }
186 Result = XboxVmpUnmapVideoMemory(
187 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
188 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
189 RequestPacket->StatusBlock);
190 break;
191
192 case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
193 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES\n");
194 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
195 {
196 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
197 return TRUE;
198 }
199 Result = XboxVmpQueryNumAvailModes(
200 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
201 (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
202 RequestPacket->StatusBlock);
203 break;
204
205 case IOCTL_VIDEO_QUERY_AVAIL_MODES:
206 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_AVAIL_MODES\n");
207 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
208 {
209 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
210 return TRUE;
211 }
212 Result = XboxVmpQueryAvailModes(
213 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
214 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
215 RequestPacket->StatusBlock);
216 break;
217
218 case IOCTL_VIDEO_QUERY_CURRENT_MODE:
219 VideoPortDebugPrint(Trace, "XboxVmpStartIO IOCTL_VIDEO_QUERY_CURRENT_MODE\n");
220 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
221 {
222 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
223 return TRUE;
224 }
225 Result = XboxVmpQueryCurrentMode(
226 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
227 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
228 RequestPacket->StatusBlock);
229 break;
230
231 default:
232 VideoPortDebugPrint(Warn, "XboxVmpStartIO 0x%x not implemented\n");
233 RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
234 return FALSE;
235 }
236
237 if (Result)
238 {
239 RequestPacket->StatusBlock->Status = NO_ERROR;
240 }
241
242 return TRUE;
243 }
244
245 /*
246 * XboxVmpResetHw
247 *
248 * This function is called to reset the hardware to a known state.
249 */
250
251 BOOLEAN NTAPI
252 XboxVmpResetHw(
253 PVOID DeviceExtension,
254 ULONG Columns,
255 ULONG Rows)
256 {
257 VideoPortDebugPrint(Trace, "XboxVmpResetHw\n");
258
259 if (! XboxVmpResetDevice((PXBOXVMP_DEVICE_EXTENSION) DeviceExtension, NULL))
260 {
261 return FALSE;
262 }
263
264 return TRUE;
265 }
266
267 /*
268 * XboxVmpGetPowerState
269 *
270 * Queries whether the device can support the requested power state.
271 */
272
273 VP_STATUS NTAPI
274 XboxVmpGetPowerState(
275 PVOID HwDeviceExtension,
276 ULONG HwId,
277 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
278 {
279 VideoPortDebugPrint(Error, "XboxVmpGetPowerState is not supported\n");
280
281 return ERROR_INVALID_FUNCTION;
282 }
283
284 /*
285 * XboxVmpSetPowerState
286 *
287 * Sets the power state of the specified device
288 */
289
290 VP_STATUS NTAPI
291 XboxVmpSetPowerState(
292 PVOID HwDeviceExtension,
293 ULONG HwId,
294 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
295 {
296 VideoPortDebugPrint(Error, "XboxVmpSetPowerState not supported\n");
297
298 return ERROR_INVALID_FUNCTION;
299 }
300
301 /*
302 * VBESetCurrentMode
303 *
304 * Sets the adapter to the specified operating mode.
305 */
306
307 BOOLEAN FASTCALL
308 XboxVmpSetCurrentMode(
309 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
310 PVIDEO_MODE RequestedMode,
311 PSTATUS_BLOCK StatusBlock)
312 {
313 if (0 != RequestedMode->RequestedMode)
314 {
315 return FALSE;
316 }
317
318 /* Nothing to do, really. We only support a single mode and we're already
319 in that mode */
320 return TRUE;
321 }
322
323 /*
324 * XboxVmpResetDevice
325 *
326 * Resets the video hardware to the default mode, to which it was initialized
327 * at system boot.
328 */
329
330 BOOLEAN FASTCALL
331 XboxVmpResetDevice(
332 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
333 PSTATUS_BLOCK StatusBlock)
334 {
335 /* There is nothing to be done here */
336
337 return TRUE;
338 }
339
340 /*
341 * XboxVmpMapVideoMemory
342 *
343 * Maps the video hardware frame buffer and video RAM into the virtual address
344 * space of the requestor.
345 */
346
347 BOOLEAN FASTCALL
348 XboxVmpMapVideoMemory(
349 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
350 PVIDEO_MEMORY RequestedAddress,
351 PVIDEO_MEMORY_INFORMATION MapInformation,
352 PSTATUS_BLOCK StatusBlock)
353 {
354 PHYSICAL_ADDRESS FrameBuffer;
355 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
356 SYSTEM_BASIC_INFORMATION BasicInfo;
357 ULONG Length;
358
359 /* FIXME: this should probably be done differently, without native API */
360 StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
361
362 FrameBuffer.u.HighPart = 0;
363 if (ZwQuerySystemInformation(SystemBasicInformation,
364 (PVOID) &BasicInfo,
365 sizeof(SYSTEM_BASIC_INFORMATION),
366 &Length) == NO_ERROR)
367 {
368 FrameBuffer.u.LowPart = BasicInfo.HighestPhysicalPageNumber * PAGE_SIZE;
369 }
370 else
371 {
372 VideoPortDebugPrint(Error, "ZwQueryBasicInformation failed, assuming 64MB total memory\n");
373 FrameBuffer.u.LowPart = 60 * 1024 * 1024;
374 }
375
376 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart;
377 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
378 MapInformation->VideoRamLength = 4 * 1024 * 1024;
379 VideoPortMapMemory(DeviceExtension, FrameBuffer,
380 &MapInformation->VideoRamLength, &inIoSpace,
381 &MapInformation->VideoRamBase);
382
383 MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
384 MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
385
386 /* Tell the nVidia controller about the framebuffer */
387 *((PULONG)((char *) DeviceExtension->VirtControlStart + CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart;
388
389 VideoPortDebugPrint(Info, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n",
390 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase);
391
392 return TRUE;
393 }
394
395 /*
396 * VBEUnmapVideoMemory
397 *
398 * Releases a mapping between the virtual address space and the adapter's
399 * frame buffer and video RAM.
400 */
401
402 BOOLEAN FASTCALL
403 XboxVmpUnmapVideoMemory(
404 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
405 PVIDEO_MEMORY VideoMemory,
406 PSTATUS_BLOCK StatusBlock)
407 {
408 VideoPortUnmapMemory(DeviceExtension, VideoMemory->RequestedVirtualAddress,
409 NULL);
410
411 return TRUE;
412 }
413
414 /*
415 * XboxVmpQueryNumAvailModes
416 *
417 * Returns the number of video modes supported by the adapter and the size
418 * in bytes of the video mode information, which can be used to allocate a
419 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
420 */
421
422 BOOLEAN FASTCALL
423 XboxVmpQueryNumAvailModes(
424 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
425 PVIDEO_NUM_MODES Modes,
426 PSTATUS_BLOCK StatusBlock)
427 {
428 Modes->NumModes = 1;
429 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
430 StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
431 return TRUE;
432 }
433
434 static BOOLEAN
435 ReadfromSMBus(UCHAR Address, UCHAR bRegister, UCHAR Size, ULONG *Data_to_smbus)
436 {
437 int nRetriesToLive=50;
438
439 while (0 != (VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0)) & 0x0800))
440 {
441 ; /* Franz's spin while bus busy with any master traffic */
442 }
443
444 while (0 != nRetriesToLive--)
445 {
446 UCHAR b;
447 int temp;
448
449 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 4), (Address << 1) | 1);
450 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 8), bRegister);
451
452 temp = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 0));
453 VideoPortWritePortUshort((PUSHORT) (I2C_IO_BASE + 0), temp); /* clear down all preexisting errors */
454
455 switch (Size)
456 {
457 case 4:
458 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0d); /* DWORD modus ? */
459 break;
460 case 2:
461 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0b); /* WORD modus */
462 break;
463 default:
464 VideoPortWritePortUchar((PUCHAR) (I2C_IO_BASE + 2), 0x0a); // BYTE
465 break;
466 }
467
468 b = 0;
469
470 while (0 == (b & 0x36))
471 {
472 b = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 0));
473 }
474
475 if (0 != (b & 0x24))
476 {
477 /* printf("I2CTransmitByteGetReturn error %x\n", b); */
478 }
479
480 if(0 == (b & 0x10))
481 {
482 /* printf("I2CTransmitByteGetReturn no complete, retry\n"); */
483 }
484 else
485 {
486 switch (Size)
487 {
488 case 4:
489 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
490 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
491 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
492 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
493 VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 9));
494 break;
495 case 2:
496 *Data_to_smbus = VideoPortReadPortUshort((PUSHORT) (I2C_IO_BASE + 6));
497 break;
498 default:
499 *Data_to_smbus = VideoPortReadPortUchar((PUCHAR) (I2C_IO_BASE + 6));
500 break;
501 }
502
503
504 return TRUE;
505 }
506 }
507
508 return FALSE;
509 }
510
511
512 static BOOLEAN
513 I2CTransmitByteGetReturn(UCHAR bPicAddressI2cFormat, UCHAR bDataToWrite, ULONG *Return)
514 {
515 return ReadfromSMBus(bPicAddressI2cFormat, bDataToWrite, 1, Return);
516 }
517
518 /*
519 * XboxVmpQueryAvailModes
520 *
521 * Returns information about each video mode supported by the adapter.
522 */
523
524 BOOLEAN FASTCALL
525 XboxVmpQueryAvailModes(
526 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
527 PVIDEO_MODE_INFORMATION VideoMode,
528 PSTATUS_BLOCK StatusBlock)
529 {
530 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock);
531 }
532
533 /*
534 * VBEQueryCurrentMode
535 *
536 * Returns information about current video mode.
537 */
538
539 BOOLEAN FASTCALL
540 XboxVmpQueryCurrentMode(
541 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
542 PVIDEO_MODE_INFORMATION VideoMode,
543 PSTATUS_BLOCK StatusBlock)
544 {
545 ULONG AvMode = 0;
546
547 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
548 VideoMode->ModeIndex = 0;
549 if (I2CTransmitByteGetReturn(0x10, 0x04, &AvMode))
550 {
551 if (1 == AvMode) /* HDTV */
552 {
553 VideoMode->VisScreenWidth = 720;
554 }
555 else
556 {
557 /* FIXME Other possible values of AvMode:
558 * 0 - AV_SCART_RGB
559 * 2 - AV_VGA_SOG
560 * 4 - AV_SVIDEO
561 * 6 - AV_COMPOSITE
562 * 7 - AV_VGA
563 * other AV_COMPOSITE
564 */
565 VideoMode->VisScreenWidth = 640;
566 }
567 }
568 else
569 {
570 VideoMode->VisScreenWidth = 640;
571 }
572 VideoMode->VisScreenHeight = 480;
573 VideoMode->ScreenStride = VideoMode->VisScreenWidth * 4;
574 VideoMode->NumberOfPlanes = 1;
575 VideoMode->BitsPerPlane = 32;
576 VideoMode->Frequency = 1;
577 VideoMode->XMillimeter = 0; /* FIXME */
578 VideoMode->YMillimeter = 0; /* FIXME */
579 VideoMode->NumberRedBits = 8;
580 VideoMode->NumberGreenBits = 8;
581 VideoMode->NumberBlueBits = 8;
582 VideoMode->RedMask = 0xff0000;
583 VideoMode->GreenMask = 0x00ff00;
584 VideoMode->BlueMask = 0x0000ff;
585 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth;
586 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight;
587 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
588 VIDEO_MODE_NO_OFF_SCREEN;
589 VideoMode->DriverSpecificAttributeFlags = 0;
590
591 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
592
593 return TRUE;
594 }
595
596 /* EOF */