xbox-kernel/private/ntos/av/modeset.c
2020-09-30 17:17:25 +02:00

1484 lines
32 KiB
C

/*++
Copyright (c) Microsoft Corporation
Module Name:
modeset.c
Abstract:
Programs the CRTC to stream the frame buffer to the tv encoder.
--*/
#include "avp.h"
#include "xpcicfg.h"
#if DBG
ULONG AvpDump;
#endif
DECLSPEC_STICKY ULONG AvpCurrentMode;
ULONG AvpMacrovisionMode;
ULONG AvpCGMS;
ULONG AvpCapabilities;
//
// Address for the frame saved across reboots and mode changes
//
DECLSPEC_STICKY PVOID AvpSavedDataAddress;
//
// Supported frame buffer modes.
//
#define D3DFMT_LIN_A1R5G5B5 0x00000010
#define D3DFMT_LIN_X1R5G5B5 0x0000001C
#define D3DFMT_LIN_R5G6B5 0x00000011
#define D3DFMT_LIN_A8R8G8B8 0x00000012
#define D3DFMT_LIN_X8R8G8B8 0x0000001E
ULONG
AvpCalcWSSCRC(
IN ULONG Value
)
/*++
Routine Description:
Sets various encoder options.
Arguments:
Value - the 20 bit number to calculate CRC for
Return Value:
The value plus the proper CRC
--*/
{
ULONG CRC;
ULONG i, lsb, msb;
CRC = 0x3F;
// This can ultimately be done more efficiently, but why?
for (i = 0; i < 14; i++)
{
lsb = (CRC & 0x01) ^ ((Value >> i) & 0x01);
msb = ((CRC >> 5) & 0x01) ^ lsb;
CRC = (CRC >> 1) & 0x0F;
CRC |= msb << 4;
CRC |= lsb << 5;
}
return Value | (CRC << 14);
}
VOID
AvpSetWSSBits(
IN PVOID RegisterBase
)
/*++
Routine Description:
Uses the current mode and macrovision state to correctly
set the WSS and CGMS bits.
Arguments:
RegisterBase - base address of the gpu hw registers
Return Value:
None
--*/
{
ULONG OutputMode;
ULONG WSS;
OutputMode = AvpCurrentMode & AV_MODE_OUT_MASK;
WSS = 0;
if (OutputMode == AV_MODE_OUT_480SDTV)
{
if (AvpCurrentMode & AV_MODE_FLAGS_WSS)
{
WSS |= 0x00000001;
}
WSS |= AvpCGMS << 6;
WSS |= (AvpMacrovisionMode & 0x01) << 9;
WSS |= (AvpMacrovisionMode & 0x02) << 7;
WSS = AvpCalcWSSCRC(WSS);
}
// 525 should be 576
else if (OutputMode == AV_MODE_OUT_525SDTV)
{
if (AvpCurrentMode & AV_MODE_FLAGS_WSS)
{
WSS |= 0x00000007;
}
else
{
WSS |= 0x00000008;
}
WSS |= AvpCGMS << 12;
}
// Do nothing.
else
{
return;
}
#ifdef FOCUS
// Set the registers.
SMB_WR(RegisterBase, 0x85, (UCHAR)((WSS & 0x0F)));
SMB_WR(RegisterBase, 0x84, (UCHAR)((WSS >> 4) & 0xFF));
SMB_WR(RegisterBase, 0x83, (UCHAR)((WSS >> 12) & 0xFF));
SMB_WR(RegisterBase, 0x88, (UCHAR)((WSS & 0x0F)));
SMB_WR(RegisterBase, 0x87, (UCHAR)((WSS >> 4) & 0xFF));
SMB_WR(RegisterBase, 0x86, (UCHAR)((WSS >> 12) & 0xFF));
#else !FOCUS
// Set the registers.
SMB_WR(RegisterBase, 0x60, (UCHAR)(0xC0 | (WSS & 0x0F)));
SMB_WR(RegisterBase, 0x62, (UCHAR)((WSS >> 4) & 0xFF));
SMB_WR(RegisterBase, 0x64, (UCHAR)((WSS >> 12) & 0xFF));
#endif !FOCUS
}
VOID
AvpEnableSCART(
IN PVOID RegisterBase,
IN ULONG iTV
)
/*++
Routine Description:
Do all of the work to enable SCART, assuming that
it has already been set up in the mode-set code.
Arguments:
RegisterBase - base address of the gpu hw registers
iTV - tv encoder table index.
Return Value:
None
--*/
{
#ifdef FOCUS
const UCHAR *pByte, *pByteMax;
ULONG i;
UCHAR Data;
pByte = AvpFocusRGBEnable[2];
pByteMax = pByte + sizeof(AvpFocusRGBEnable[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
Data = SMB_RD(RegisterBase, AvpFocusRGBEnable[0][i]);
Data &= ~AvpFocusRGBEnable[1][i];
SMB_WR(RegisterBase, AvpFocusRGBEnable[0][i], Data | *pByte);
}
#else
const UCHAR *pByte, *pByteMax;
ULONG i;
pByte = AvpSCARTEnable[iTV];
pByteMax = pByte + sizeof(AvpSCARTEnable[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SMB_WR(RegisterBase, AvpSCARTEnable[0][i], *pByte);
}
#endif
}
//****************************************************************************
//
// Mode set code.
//
//****************************************************************************
ULONG
AvSetDisplayMode(
IN PVOID RegisterBase,
IN ULONG Step,
IN ULONG Mode, // mode from the AV_MODE list (avmode.h)
IN ULONG Format, // D3DFORMAT
IN ULONG Pitch,
IN ULONG FrameBuffer
)
/*++
Routine Description:
Initializes the CRTC to stream the framebuffer to the
tv encoder.
Arguments:
RegisterBase - base address of the gpu hw registers
Step - what step we're currently doing in the mode-set process
Mode - the AV_PIPELINE mode to initialize
Format - format of the frame buffer
Pitch - the pitch of the frame buffer
FrameBuffer - physical address of the frame buffer
Return Value:
None
--*/
{
ULONG GeneralControl;
ULONG Value;
ULONG i;
ULONG LgsValue;
UCHAR Data;
UCHAR Register;
const UCHAR *pByte;
const UCHAR *pByteMax;
const ULONG *pLong;
const ULONG *pLongMax;
UCHAR CR28Depth;
UCHAR CR13Value;
ULONG OutputMode;
ULONG iRegister;
ULONG iCRTC;
ULONG iTV;
UCHAR DACs;
USHORT FocusValue;
NTSTATUS Status;
//
// Decode the mode.
//
// If the mode is zero then we just set this to NTSC and turn
// the DACs off.
//
if (Mode == AV_MODE_OFF)
{
Mode = AV_MODE_640x480_TO_NTSC_M_YC
| AV_MODE_FLAGS_DACA_DISABLE
| AV_MODE_FLAGS_DACB_DISABLE
| AV_MODE_FLAGS_DACC_DISABLE
| AV_MODE_FLAGS_DACD_DISABLE;
}
OutputMode = Mode & AV_MODE_OUT_MASK;
iRegister = (Mode & 0x00FF0000) >> 16;
iCRTC = (Mode & 0x0000FF00) >> 8;
iTV = (Mode & 0x0000007F);
DACs = (UCHAR)((Mode & 0x0F000000) >> 24);
//
// Get the format-specific parameters.
//
switch(Format)
{
case D3DFMT_LIN_A1R5G5B5:
case D3DFMT_LIN_X1R5G5B5:
GeneralControl = 0x00100030;
CR28Depth = 2;
break;
case D3DFMT_LIN_R5G6B5:
GeneralControl = 0x00101030;
CR28Depth = 2;
break;
case D3DFMT_LIN_A8R8G8B8:
case D3DFMT_LIN_X8R8G8B8:
GeneralControl = 0x00100030;
CR28Depth = 3;
break;
}
// Pitch is always measured in bytes.
Pitch /= 8;
//
// Handle format-only changes, very little has to be munged
// for this case.
//
if (AvpCurrentMode == Mode)
{
ASSERT(Step == 0);
// Reset the general control register.
REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl);
// Unlock the CRTC
CRTC_WR(RegisterBase, NV_CIO_SR_LOCK_INDEX, NV_CIO_SR_UNLOCK_RW_VALUE);
// Set the pitch.
CRTC_WR(RegisterBase, 0x13, (UCHAR)(Pitch & 0xFF));
CRTC_WR(RegisterBase, 0x19, (UCHAR)((Pitch & 0x700) >> 3));
// Slave the CRTC to the encoder with the proper color depth.
CRTC_WR(RegisterBase, 0x28, 0x80 | CR28Depth);
// Set the frame buffer pointer.
REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer);
// Make sure the flicker filter and luma filter gets reset to their
// default values.
//
AvSendTVEncoderOption(RegisterBase,
AV_OPTION_FLICKER_FILTER,
5,
NULL);
AvSendTVEncoderOption(RegisterBase,
AV_OPTION_ENABLE_LUMA_FILTER,
FALSE,
NULL);
AvpCurrentMode = Mode;
return 0;
}
//
// Do a full mode change
//
else
{
ASSERT(Step == 0);
// Use the hardware to disable the DACs.
_outp(XPCICFG_LPCBRIDGE_IO_REGISTER_BASE_0 + 0xD3, 0x5);
//
// Wait for any previous lightgun changes have been processed by the
// hardware before making any other changes.
//
do
{
LgsValue = REG_RD32(RegisterBase, NV_PRAMDAC_LGS_TRANSFER);
}
while (!(LgsValue & 0x01) != !(LgsValue & 0x10));
//
// Unlock the CRTC and the tv encoder
//
CRTC_WR(RegisterBase, 0x1F, 0x57);
CRTC_WR(RegisterBase, 0x21, 0xFF);
// Unslave the CRTC from the tv encoder clock for now.
CRTC_WR(RegisterBase, 0x28, 0x00);
// Disable pclk stalling.
REG_WR32(RegisterBase, NV_PRAMDAC_FP_DEBUG_0, 0x21121111);
#ifndef FOCUS
// Do a reset and turn off dacs.
SMB_WR(RegisterBase, 0xBA, 0x80);
SMB_WR(RegisterBase, 0xBA, 0x3F);
// Enable tv encoder register munging.
SMB_WR(RegisterBase, 0x6C, 0x46);
#endif
// Wait 1 microsecond
KeStallExecutionProcessor(1);
#ifndef FOCUS
// Reset the timing.
SMB_WR(RegisterBase, 0x6C, 0xC6);
#endif
//
// Program the TV encoder
//
// !!! PERF we can write runs of registers more efficiently.
if (OutputMode == AV_MODE_OUT_HDTV)
{
pByte = AvpHDTVRegisters[iTV];
pByteMax = pByte + sizeof(AvpHDTVRegisters[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SMB_WR(RegisterBase, AvpHDTVRegisters[0][i], *pByte);
}
}
else
{
#ifndef FOCUS
pByte = AvpCommonTVRegisters[1];
pByteMax = pByte + sizeof(AvpCommonTVRegisters[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SMB_WR(RegisterBase, AvpCommonTVRegisters[0][i], *pByte);
}
#endif !FOCUS
pByte = AvpTVRegisters[iTV];
pByteMax = pByte + sizeof(AvpTVRegisters[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SMB_WR(RegisterBase, AvpTVRegisters[0][i], *pByte);
}
// Hack for NTSC-M and NTSC-J YPrPb outputs.
if (XboxGameRegion == XC_GAME_REGION_JAPAN && (iTV == 0xD || iTV == 0xE || iTV == 0x12))
{
#ifdef FOCUS
SMB_WRW(RegisterBase, 0x4E, 0x013C);
#else !FOCUS
SMB_WR(RegisterBase, 0xA2, 0x08);
SMB_WR(RegisterBase, 0xA4, 0xF0);
SMB_WR(RegisterBase, 0xAC, 0x9A);
#endif !FOCUS
}
}
// Enable SCART
if (Mode & AV_MODE_FLAGS_SCART)
{
// Twiddle the other registers.
AvpEnableSCART(RegisterBase, iTV);
}
// Always set the SCART aspect ratio signal because it may be
// handy to have for future AV packs.
//
_outp(XPCICFG_LPCBRIDGE_IO_REGISTER_BASE_0 + 0xD6,
(Mode & AV_MODE_FLAGS_WSS) ? 0x5 : 0x4);
// We are now allowed to always have RGB output on...so just
// set this pin and leave it.
//
_outp(XPCICFG_LPCBRIDGE_IO_REGISTER_BASE_0 + 0xD8, 0x4);
//
// Set up the RAMDAC to stream from the frame buffer.
//
// Set up the clocks to always use the tv-encoder as the
// source of the pclk and to divice pclk by 2 for the
// vclk.
//
REG_OR32(RegisterBase, NV_PRAMDAC_PLL_COEFF_SELECT, 0x10020000);
// Unlock the VGA registers.
REG_WR08(RegisterBase, NV_PRMVIO_VSE2, 0x01);
// Set random output bits.
REG_WR08(RegisterBase, NV_PRMVIO_MISC__WRITE, 0xE3);
// Set up the general control register. This varies depending on
// the frame buffer format.
//
// The interesting bits:
//
// 5: 4 - 3 = PIXMIX bit is ignored
// 12:12 - Selects between 5-5-5 (off) and 5-6-5 (on)
// 16:16 - BLK_PEDSTL ON (??)
//
REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl);
pLong = AvpRegisters[iRegister];
pLongMax = pLong + sizeof(AvpRegisters[0]) / sizeof(ULONG);
// Set the rest.
for (i = 0; pLong < pLongMax; pLong++, i++)
{
REG_WR32(RegisterBase, AvpRegisters[0][i], *pLong);
}
// SCART shovels its output in RGB mode.
if (Mode & AV_MODE_FLAGS_SCART)
{
REG_WR32(RegisterBase, 0x680630, 0);
REG_WR32(RegisterBase, 0x6808C4, 0);
REG_WR32(RegisterBase, 0x68084C, 0);
}
//
// Set the initial VGA registers.
//
// Write the SRX registers.
pByte = AvpSRXRegisters;
pByteMax = pByte + sizeof(AvpSRXRegisters);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SRX_WR(RegisterBase, (UCHAR)i, *pByte);
}
// Write the GRX registers
pByte = AvpGRXRegisters;
pByteMax = pByte + sizeof(AvpGRXRegisters);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
GRX_WR(RegisterBase, (UCHAR)i, *pByte);
}
// Write the ARX registers. This will cause the screen to blank
// until the registers have all been written.
//
REG_RD08(RegisterBase, NV_PRMCIO_INP0__COLOR);
pByte = AvpARXRegisters;
pByteMax = pByte + sizeof(AvpARXRegisters);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
ARX_WR(RegisterBase, (UCHAR)i, *pByte);
}
// Unblank the screen.
REG_WR08(RegisterBase, NV_PRMCIO_ARX, 0x20);
//
// Program the CRTC
//
// Unlock the rest of the CRTC.
CRTC_WR(RegisterBase, 0x11, 0x00);
pByte = AvpCRTCRegisters[iCRTC];
pByteMax = pByte + sizeof(AvpCRTCRegisters[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
Register = AvpCRTCRegisters[0][i];
Data = *pByte;
if (Register == 0x13)
{
Data = (UCHAR)(Pitch & 0xFF);
}
else if (Register == 0x19)
{
Data |= (UCHAR)((Pitch & 0x700) >> 3);
}
else if (Register == 0x25)
{
Data |= (UCHAR)((Pitch & 0x800) >> 6);
}
CRTC_WR(RegisterBase, AvpCRTCRegisters[0][i], Data);
}
//
// Commit the light gun changes.
//
REG_WR32(RegisterBase, NV_PRAMDAC_LGS_TRANSFER, !(LgsValue & 0x01) & 0x01);
//
// Sync up the CRTC with the tv encoder.
//
// Slave the CRTC to the encoder.
CRTC_WR(RegisterBase, 0x28, 0x80 | CR28Depth);
// Wait a few lines until everything gets in sync.
for (i = 0; i < 3; i++)
{
while ((REG_RD32(RegisterBase, NV_PRMCIO_INP0__COLOR) & 0x08) == 0x08)
;
while ((REG_RD32(RegisterBase, NV_PRMCIO_INP0__COLOR) & 0x08) == 0x00)
;
}
// Set FP_DEBUG_0 which will also enable pclk stalling.
REG_WR32(RegisterBase, NV_PRAMDAC_FP_DEBUG_0, AvpFpDebug0[iRegister]);
//
// Afterglow
//
// Wait 1 microsecond
KeStallExecutionProcessor(1);
#ifndef FOCUS
// Reset the timing.
Data = SMB_RD(RegisterBase, 0x6C);
SMB_WR(RegisterBase, 0x6C, Data | 0x80);
// Enable the DACs.
SMB_WR(RegisterBase, 0xBA, 0x20 | DACs);
#endif
// Set the frame buffer pointer.
REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer);
AvpCurrentMode = Mode;
AvpSetWSSBits(RegisterBase);
return 0;
}
}
VOID
AvpSetMacrovisionMode(
IN PVOID RegisterBase,
IN ULONG MacrovisionMode
)
/*++
Routine Description:
Sets up macrovision.
Arguments:
RegisterBase - base address of the gpu hw registers
MacrovisionMode -
Return Value:
None
--*/
{
const UCHAR *Registers = NULL;
UCHAR Tv;
ULONG i,c ;
ULONG OutputMode;
UCHAR Data;
#ifdef FOCUS
UCHAR CCEnable;
#endif
Tv = (UCHAR)(AvpCurrentMode & 0xFF);
OutputMode = AvpCurrentMode & AV_MODE_OUT_MASK;
// 525 should be 576
if (OutputMode != AV_MODE_OUT_480SDTV && OutputMode != AV_MODE_OUT_525SDTV)
{
// No macrovision.
return;
}
if (OutputMode == AV_MODE_OUT_480SDTV)
{
switch(MacrovisionMode)
{
case AV_MV_OFF:
Registers = AvpMacrovision[AVP_MV_NTSC_MODE_0];
break;
case AV_MV_AGC_ONLY:
Registers = AvpMacrovision[AVP_MV_NTSC_MODE_1];
break;
case AV_MV_TWO_STRIPES_PLUS_AGC:
Registers = AvpMacrovision[AVP_MV_NTSC_MODE_2];
break;
case AV_MV_FOUR_STRIPES_PLUS_AGC:
default:
Registers = AvpMacrovision[AVP_MV_NTSC_MODE_3];
break;
}
// Sploich the registers.
c = sizeof(AvpMacrovision[0]);
#ifdef FOCUS
CCEnable = (SMB_RDW(RegisterBase, 0xB2) >> 8 ) & 0xC0;
#endif
for (i = 0; i < c; i++)
{
#ifdef FOCUS
if (AvpMacrovision[0][i] == 0xB3)
{
SMB_WR(RegisterBase, AvpMacrovision[0][i], Registers[i] | CCEnable);
}
else
#endif
{
SMB_WR(RegisterBase, AvpMacrovision[0][i], Registers[i]);
}
}
}
else
{
switch(MacrovisionMode)
{
case AV_MV_OFF:
Registers = AvpMacrovisionPAL[AVP_MV_PAL_MODE_0];
break;
case AV_MV_AGC_ONLY:
default:
Registers = AvpMacrovisionPAL[AVP_MV_PAL_MODE_1];
break;
}
// Sploich the registers.
c = sizeof(AvpMacrovisionPAL[0]);
for (i = 0; i < c; i++)
{
SMB_WR(RegisterBase, AvpMacrovisionPAL[0][i], Registers[i]);
}
}
}
VOID
AvpCommitMacrovisionMode(
IN PVOID RegisterBase,
IN ULONG MacrovisionMode
)
/*++
Routine Description:
Commits the macrovision changes.
Arguments:
RegisterBase - base address of the gpu hw registers
MacrovisionMode -
Return Value:
None
--*/
{
const UCHAR *Registers = NULL;
UCHAR Tv;
ULONG i,c ;
ULONG OutputMode;
USHORT Data;
Tv = (UCHAR)(AvpCurrentMode & 0xFF);
OutputMode = AvpCurrentMode & AV_MODE_OUT_MASK;
#ifdef FOCUS
if (OutputMode == AV_MODE_OUT_480SDTV
|| OutputMode == AV_MODE_OUT_525SDTV
|| (AvpCurrentMode & 0xFF0000FF) == 0x88000001)
{
Data = SMB_RDW(RegisterBase, 0x0E);
if (MacrovisionMode == AV_MV_OFF)
{
SMB_WRW(RegisterBase, 0x0E, Data & ~0x8000);
}
else
{
SMB_WRW(RegisterBase, 0x0E, Data | 0x8000);
}
}
#else !FOCUS
if (OutputMode == AV_MODE_OUT_480SDTV)
{
switch(MacrovisionMode)
{
case AV_MV_OFF:
Registers = AvpMacrovisionEnable[AVP_MV_NTSC_MODE_0];
break;
case AV_MV_AGC_ONLY:
Registers = AvpMacrovisionEnable[AVP_MV_NTSC_MODE_1];
break;
case AV_MV_TWO_STRIPES_PLUS_AGC:
Registers = AvpMacrovisionEnable[AVP_MV_NTSC_MODE_2];
break;
case AV_MV_FOUR_STRIPES_PLUS_AGC:
default:
Registers = AvpMacrovisionEnable[AVP_MV_NTSC_MODE_3];
break;
}
// Sploich the registers.
c = sizeof(AvpMacrovisionEnable[0]);
for (i = 0; i < c; i++)
{
SMB_WR(RegisterBase, AvpMacrovisionEnable[0][i], Registers[i]);
}
}
#endif !FOCUS
}
ULONG
AvSMCVideoModeToAVPack(
ULONG VideoMode
)
/*++
Routine Description:
Converts an SMC video mode to an AV pack constant.
Arguments:
VideoMode - Video mode as returned by SMC_COMMAND_VIDEO_MODE.
Return Value:
Returns the AV pack constant.
--*/
{
ULONG Result;
switch(VideoMode)
{
case 0x0:
// DVT-1 boards will always select this mode. That should
// be ok because only the composite output will be enabled
// on an NTSC-M system.
//
Result = AV_PACK_SCART;
break;
case 0x1:
Result = AV_PACK_HDTV;
break;
case 0x2:
Result = AV_PACK_VGA;
break;
case 0x3:
Result = AV_PACK_RFU;
break;
case 0x4:
Result = AV_PACK_SVIDEO;
break;
case 0x6:
Result = AV_PACK_STANDARD;
break;
default:
case 0x7:
Result = AV_PACK_NONE;
break;
}
return Result;
}
ULONG
AvpQueryAvCapabilities(
)
/*++
Routine Description:
Reads the AV settings from the AV pack and EEPROM.
Arguments:
None
Return Value:
The AV caps flags.
--*/
{
ULONG VideoMode;
ULONG Type;
ULONG ResultLength;
NTSTATUS status;
ULONG Result;
Result = AvSMCVideoModeToAVPack(HalBootSMCVideoMode);
// Get the region from the EEPROM.
status = ExQueryNonVolatileSetting(XC_FACTORY_AV_REGION,
&Type,
&VideoMode,
sizeof(VideoMode),
&ResultLength);
// Didn't work? Default to NTSC.
if (status != STATUS_SUCCESS || ResultLength != sizeof(VideoMode))
{
VideoMode = AV_STANDARD_NTSC_M | AV_FLAGS_60Hz;
}
Result |= VideoMode & (AV_STANDARD_MASK | AV_REFRESH_MASK);
// Get the user settings.
status = ExQueryNonVolatileSetting(XC_VIDEO_FLAGS,
&Type,
&VideoMode,
sizeof(VideoMode),
&ResultLength);
// Didn't work? Default to no options.
if (status != STATUS_SUCCESS || ResultLength != sizeof(VideoMode))
{
VideoMode = 0;
}
Result |= VideoMode & ~(AV_STANDARD_MASK | AV_PACK_MASK);
return Result;
}
void
AvSendTVEncoderOption(
IN PVOID RegisterBase,
IN ULONG Option,
IN ULONG Param,
OUT PULONG Result
)
/*++
Routine Description:
Sets various encoder options.
Arguments:
RegisterBase - base address of the gpu hw registers
Return Value:
None
--*/
{
ULONG OutputMode;
UCHAR Data, OldValue;
ULONG iTV, i;
USHORT WValue;
const UCHAR *pByte;
const UCHAR *pByteMax;
OutputMode = AvpCurrentMode & AV_MODE_OUT_MASK;
iTV = (AvpCurrentMode & 0x0000007F);
if (!RegisterBase)
{
RegisterBase = (void *)XPCICFG_GPU_MEMORY_REGISTER_BASE_0;
}
switch (Option) {
case AV_OPTION_BLANK_SCREEN:
REG_WR08(RegisterBase, NV_PRMVIO_SRX, 0x01);
if (Param)
{
REG_WR08(RegisterBase, NV_PRMVIO_SR_RESET, 0x21);
}
else
{
REG_WR08(RegisterBase, NV_PRMVIO_SR_RESET, 0x01);
// Renable the DACs.
_outp(XPCICFG_LPCBRIDGE_IO_REGISTER_BASE_0 + 0xD3, 0x4);
}
break;
case AV_OPTION_MACROVISION_MODE:
AvpSetMacrovisionMode(RegisterBase, Param);
AvpMacrovisionMode = Param;
break;
case AV_OPTION_MACROVISION_COMMIT:
AvpCommitMacrovisionMode(RegisterBase, Param);
AvpSetWSSBits(RegisterBase);
break;
case AV_OPTION_ENABLE_CC:
if (OutputMode == AV_MODE_OUT_480SDTV)
{
#ifdef FOCUS
WValue = SMB_RDW(RegisterBase, 0xB2);
SMB_WRW(RegisterBase, 0xB2, WValue | 0xC000);
#else !FOCUS
Data = SMB_RD(RegisterBase, 0xC4);
SMB_WR(RegisterBase, 0xC4, Data | 0x38);
pByte = AvpCCRegisters[iTV];
pByteMax = pByte + sizeof(AvpCCRegisters[0]);
for (i = 0; pByte < pByteMax; pByte++, i++)
{
SMB_WR(RegisterBase, AvpCCRegisters[0][i], *pByte);
}
#endif !FOCUS
}
break;
case AV_OPTION_DISABLE_CC:
if (OutputMode == AV_MODE_OUT_480SDTV)
{
#ifdef FOCUS
WValue = SMB_RDW(RegisterBase, 0xB2);
SMB_WRW(RegisterBase, 0xB2, WValue & 0x3FFF);
#else !FOCUS
UCHAR Data = SMB_RD(RegisterBase, 0xC4);
// Clear ECC enable bits.
SMB_WR(RegisterBase, 0xC4, Data & ~0x30);
#endif !FOCUS
}
break;
case AV_OPTION_SEND_CC_DATA:
if (OutputMode == AV_MODE_OUT_480SDTV)
{
#ifdef FOCUS
USHORT Value;
Value = (USHORT)(*Result & 0xFF);
Value |= (USHORT)((*Result >> 16) & 0xFF) << 8;
if (Param & 1)
{
SMB_WRW(RegisterBase, 0xAE, Value);
}
else
{
SMB_WRW(RegisterBase, 0xB0, Value);
}
#else !FOCUS
if (Param & 1)
{
SMB_WR(RegisterBase, 0xC0, (UCHAR)(*Result >> 16));
SMB_WR(RegisterBase, 0xC2, (UCHAR)*Result);
}
else
{
SMB_WR(RegisterBase, 0xBC, (UCHAR)(*Result >> 16));
SMB_WR(RegisterBase, 0xBE, (UCHAR)*Result);
}
#endif !FOCUS
}
break;
case AV_QUERY_CC_STATUS:
if (OutputMode == AV_MODE_OUT_480SDTV)
{
#ifdef FOCUS
*Result = 0;
if (SMB_RDW(RegisterBase, 0xAE))
{
*Result |= 0x1;
}
if (SMB_RDW(RegisterBase, 0xB0))
{
*Result |= 0x2;
}
#else !FOCUS
*Result = (SMB_RD(RegisterBase, 0x02) >> 2) & 3;
#endif !FOCUS
}
else
{
*Result = 0;
}
break;
case AV_QUERY_AV_CAPABILITIES:
AvpCapabilities = AvpQueryAvCapabilities();
*Result = AvpCapabilities;
break;
case AV_OPTION_FLICKER_FILTER:
#ifdef FOCUS
SMB_WR(RegisterBase, 0x26, (UCHAR)(Param * 16 / 5));
SMB_WR(RegisterBase, 0x27, 0);
#else !FOCUS
if (Param == 0)
{
// Turn the flicker filter off.
Data = SMB_RD(RegisterBase, 0xC8);
SMB_WR(RegisterBase, 0xC8, Data | 0x40);
}
else
{
switch(Param)
{
case 1:
Data = 0x01;
break;
case 2:
Data = 0x02;
break;
case 3:
Data = 0x03;
break;
case 4:
default:
Data = 0x00;
break;
}
// Turn the flicker filter on.
OldValue = SMB_RD(RegisterBase, 0xC8);
// Clear out the old values and clear the disable bit.
OldValue &= 0x80;
// Set the new values.
OldValue |= Data | (Data << 3);
SMB_WR(RegisterBase, 0xC8, OldValue);
// Munge the adapate FF.
SMB_WR(RegisterBase, 0x34, Param == 5 ? 0x80 : 0x00);
}
#endif !FOCUS
break;
case AV_OPTION_ZERO_MODE:
AvpCurrentMode = 0;
break;
case AV_OPTION_QUERY_MODE:
*Result = AvpCurrentMode;
break;
case AV_OPTION_ENABLE_LUMA_FILTER:
#ifndef FOCUS
Data = SMB_RD(RegisterBase, 0x96) & 0x0F;
if (Param)
{
Data |= 0x10;
}
SMB_WR(RegisterBase, 0x96, Data);
#endif
break;
case AV_OPTION_GUESS_FIELD:
#ifndef FOCUS
*Result = SMB_RD(RegisterBase, 0x06) & 0x01;
#endif
break;
case AV_QUERY_ENCODER_TYPE:
#ifdef FOCUS
*Result = AV_ENCODER_FOCUS;
#else
*Result = AV_ENCODER_CONEXANT_871;
#endif
break;
case AV_QUERY_MODE_TABLE_VERSION:
*Result = AV_MODE_TABLE_VERSION;
break;
case AV_OPTION_CGMS:
AvpCGMS = Param;
AvpSetWSSBits(RegisterBase);
break;
case AV_OPTION_WIDESCREEN:
AvpCurrentMode = (AvpCurrentMode & ~AV_MODE_FLAGS_WSS);
if (Param)
{
AvpCurrentMode |= AV_MODE_FLAGS_WSS;
}
AvpSetWSSBits(RegisterBase);
break;
default:
;
}
}
PVOID
AvGetSavedDataAddress(
VOID
)
/*++
Routine Description:
Returns sticky data address saved acress quick reboots.
Arguments:
None.
Return Value:
Saved global address.
--*/
{
return AvpSavedDataAddress;
}
VOID
AvSetSavedDataAddress(
PVOID Address
)
/*++
Routine Description:
Sets sticky data address saved acress quick reboots.
Arguments:
The address to save.
Return Value:
None.
--*/
{
AvpSavedDataAddress = Address;
}
VOID
AvRelocateSavedDataAddress(
IN PVOID NewAddress,
IN SIZE_T NumberOfBytes
)
/*++
Routine Description:
Sets sticky data address saved acress quick reboots.
Arguments:
The address to save.
Return Value:
None.
--*/
{
D3DSurface *OldSurface;
D3DSurface *NewSurface;
ASSERT(AvpSavedDataAddress != NULL);
OldSurface = (D3DSurface*)AvpSavedDataAddress;
NewSurface = (D3DSurface*)NewAddress;
//
// Copy the bits from the the old surface to the new surface, including all
// of the headers.
//
RtlCopyMemory(NewSurface, OldSurface, NumberOfBytes);
//
// Update the physical address of the surface data stored in the surface
// header.
//
NewSurface->Data = MmGetPhysicalAddress(NewSurface) +
(OldSurface->Data - MmGetPhysicalAddress(OldSurface));
//
// Wait for the write combine buffers to flush.
//
__asm sfence;
//
// Program the NV2A to scan from the new surface.
//
REG_WR32((PVOID)XPCICFG_GPU_MEMORY_REGISTER_BASE_0, NV_PCRTC_START,
NewSurface->Data);
//
// Reset the VBLANK pending flag.
//
REG_WR32((PVOID)XPCICFG_GPU_MEMORY_REGISTER_BASE_0, NV_PCRTC_INTR_0,
NV_PCRTC_INTR_0_VBLANK_RESET);
//
// Persist the new surface and update the saved data address to point at
// this surface.
//
MmPersistContiguousMemory(NewSurface, NumberOfBytes, TRUE);
AvpSavedDataAddress = NewSurface;
//
// Before freeing the old surface, loop until a VBLANK has occurred. The
// NV2A begins using the new surface at the next VBLANK.
//
while ((REG_RD32((PVOID)XPCICFG_GPU_MEMORY_REGISTER_BASE_0, NV_PCRTC_INTR_0) &
NV_PCRTC_INTR_0_VBLANK_PENDING) == 0);
//
// Free the memory used for the old surface.
//
MmFreeContiguousMemory(OldSurface);
}