📄 ltddc.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*****************************************************************************\
*
* Module Name LTDDC.C
* Project 3D RAGE PRO/LT Rage Pro
* Device Rage
* Device Rage
*
* Description Retrieves EDID data from the monitor.
*
* (c) 1997 ATI Technologies Inc. (unpublished)
*
* All rights reserved. This notice is intended as a precaution against
* inadvertent publication and does not imply publication or any waiver
* of confidentiality. The year included in the foregoing notice is the
* year of creation of the work.
*
* LOG OF CHANGES
*
* 1.0 03/30/98 [MM] Initial revision
*
\*****************************************************************************/
#include "dal.h"
#include "ltddc.h"
#include "ltcrtl.h"
#include "rprod.h"
//
// DDC routines are very time sensitive. We try to put them on the same page, so when DDC2Query() is called
// we'll have a good chance they won't be paged out.
//
//
// Allow swapping.
//
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(PAGE_DDC, DDC2Query)
#if TRUE //(_WIN32_WINNT < 0x500) [cd]:Temporary until DDC detection fixed for Win2K
#pragma alloc_text(PAGE_DDC, DDC2QueryCallback)
#pragma alloc_text(PAGE_DDC, DDC2_I2CStart)
#pragma alloc_text(PAGE_DDC, DDC2_I2CStop)
#pragma alloc_text(PAGE_DDC, DDC2_I2CWrite)
#pragma alloc_text(PAGE_DDC, DDC2_I2CWriteByte)
#pragma alloc_text(PAGE_DDC, DDC2_I2CRead)
#pragma alloc_text(PAGE_DDC, DDC2_I2CReadByte)
#pragma alloc_text(PAGE_DDC, DDC2_I2CWaitForClockLineHigh)
#endif // _WIN32_WINNT < 0x500
#pragma alloc_text(PAGE_DDC, DDC2_I2CWriteClockLine)
#pragma alloc_text(PAGE_DDC, DDC2_I2CWriteDataLine)
#pragma alloc_text(PAGE_DDC, DDC2_I2CReadClockLine)
#pragma alloc_text(PAGE_DDC, DDC2_I2CReadDataLine)
#pragma alloc_text(PAGE_DDC, WaitForVerticalBlank)
#endif // defined (ALLOC_PRAGMA)
#if FALSE //(_WIN32_WINNT >= 0x500) [cd]:Temporary until DDC detection fixed for Win2K
BOOLEAN
DDC2Query(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucQueryBuffer,
ULONG ulLength
)
//
// DESCRIPTION:
// Reads the basic EDID structure from the monitor using DDC.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucQueryBuffer Buffer where information will be stored.
// ulLength Size of the buffer to fill.
//
// RETURN VALUE:
// TRUE DDC read OK.
// FALSE DDC read failed.
//
{
I2C_FNC_TABLE i2c;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucQueryBuffer, "lpucQueryBuffer is NULL!");
i2c.WriteClockLine = DDC2_I2CWriteClockLine;
i2c.WriteDataLine = DDC2_I2CWriteDataLine;
i2c.ReadClockLine = DDC2_I2CReadClockLine;
i2c.ReadDataLine = DDC2_I2CReadDataLine;
i2c.WaitVsync = WaitForVerticalBlank;
i2c.Size = sizeof (I2C_FNC_TABLE);
if (VideoPortDDCMonitorHelper(hDDL, &i2c, lpucQueryBuffer, ulLength) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2Query: DDC query failed"));
return FALSE;
}
DALDEBUG((DALDBG_DETAIL, "DDC2Query: DDC query OK"));
//[MF]DebugDumpHex(DEBUG_NORMAL, "DDC2Query: EDID", lpucQueryBuffer, ulLength);
return TRUE;
} // DDC2Query()
#else // _WIN32_WINNT >= 0x500
BOOLEAN
DDC2Query(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucQueryBuffer,
ULONG ulLength
)
//
// DESCRIPTION:
// Reads the basic EDID structure from the monitor using DDC.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucQueryBuffer Buffer where information will be stored.
// ulLength Size of the buffer to fill.
//
// RETURN VALUE:
// TRUE DDC read OK.
// FALSE DDC read failed.
//
{
DDC2_QUERY_CONTEXT ddcQueryContext;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucQueryBuffer, "lpucQueryBuffer is NULL!");
ddcQueryContext.hGDO = hGDO;
ddcQueryContext.hDDL = hDDL;
ddcQueryContext.lpucQueryBuffer = lpucQueryBuffer;
ddcQueryContext.ulLength = ulLength;
//
// Use a low priority on our critical section because it is long enough to risk problems if we disable interrupts.
//
#ifndef CE_BUILD
return SYNCEXECUTION(hDDL, VpLowPriority, DDC2QueryCallback, (LPVOID)&ddcQueryContext);
#else
return FALSE; // DDC not supported in WinCE
#endif
} // DDC2Query()
BOOLEAN
DDC2QueryCallback(
LPDDC2_QUERY_CONTEXT lpDdcQueryContext
)
//
// DESCRIPTION:
// Allowes EDID to be read through VideoPortSynchronizeExecution().
//
// PARAMETERS:
// pDdcQueryContext Points to query context.
//
// RETURN VALUE:
// TRUE DDC read OK.
// FALSE DDC read failed.
//
// NOTE:
// Some monitors are extremely sensitive to timing during a DDC query. Adding debug print statements within
// the query routine will throw off the timing enough to confuse the monitor, causing the DDC query to fail.
//
{
ULONG ulChecksum; // EDID checksum
ULONG ulScratch; // Temp variable
ULONG ulRetryCounter;
BOOLEAN bWriteStartAddress;
LPGDO_RAGE_CRT hGDORageCRT;
DALASSERT(NULL != lpDdcQueryContext, "pDdcQueryContext is NULL!");
DALASSERT(NULL != lpDdcQueryContext->hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpDdcQueryContext->lpucQueryBuffer, "lpucQueryBuffer is NULL!");
DALASSERT(lpDdcQueryContext->ulLength >= EDID_SIZE, "ulLength is too large!");
hGDORageCRT = (LPGDO_RAGE_CRT)lpDdcQueryContext->hGDO;
// EPR 40230
// Initialize GPIO_15 and GPIO_16
// Ensure CRTC_ON bit is set.
//
vInitializeLT_GIO( lpDdcQueryContext->hGDO );
vEnsureCRTON( lpDdcQueryContext->hGDO );
//
// Initialize SDA and SCL lines to default state of released high (input).
// Drive SCL low for 15ms to switch DDC2-capable monitor to DDC2 mode.
//
DDC2_I2CWriteDataLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
1);
DDC2_I2CWriteClockLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
0);
DELAY_MILLISECONDS(15);
DDC2_I2CWriteClockLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
1);
if (DDC2_I2CWaitForClockLineHigh(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_QueryCallback: Can't switch to DDC2"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
//
// Tell the monitor that we want to talk to it and give the address we want to start with.
//
lpDdcQueryContext->lpucQueryBuffer[0] = (UCHAR)DDC2_I2C_MONITOR_ADDRESS_WRITE;
lpDdcQueryContext->lpucQueryBuffer[1] = (UCHAR)DDC2_I2C_EDID_START_ADDRESS;
// EPR 40230
// Some monitor may take longer to acknowledge this write When CRT_ON bit is
// just turned on. So we have to keep trying here until monitor acknowledge.
//
ulRetryCounter = hGDORageCRT->ulRetryCounter;
bWriteStartAddress = FALSE;
while ( ulRetryCounter > 0 && bWriteStartAddress == FALSE )
{
DELAY_MILLISECONDS(10);
bWriteStartAddress = (DDC2_I2CWrite(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
lpDdcQueryContext->lpucQueryBuffer,
2));
--ulRetryCounter;
}
if ( bWriteStartAddress == FALSE )
{
DALDEBUG((DALDBG_DETAIL, "DDC2_QueryCallback: Can't write - DDC2 not supported"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
//
// Read EDID from the monitor.
//
ZEROMEMORY(lpDdcQueryContext->lpucQueryBuffer, lpDdcQueryContext->ulLength);
if (DDC2_I2CRead(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
lpDdcQueryContext->lpucQueryBuffer,
lpDdcQueryContext->ulLength) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_QueryCallback: Can't read - DDC2 not supported"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
//[MF]DebugDumpHex(DEBUG_NORMAL, "DDC2QueryCallback: EDID", pDdcQueryContext->lpucQueryBuffer, pDdcQueryContext->ulLength);
//
// Calculate the EDID checksum. We should have 0x00 in LSB for proper EDID.
// Note: we don't have to preset EDID buffer since we know we can talk to the monitor.
//
ulChecksum = 0;
for (ulScratch = 0; ulScratch < lpDdcQueryContext->ulLength; ulScratch++)
ulChecksum += lpDdcQueryContext->lpucQueryBuffer[ulScratch];
DALDEBUG((DALDBG_DETAIL, "DDC2_QueryCallback: EDID checksum = 0x%08X", ulChecksum));
if ((ulChecksum & 0xFF) || (0 == ulChecksum))
{
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
vRestoreCRTON( lpDdcQueryContext->hGDO );
return TRUE;
} // DDC2QueryCallback()
// EPR 32286
BOOLEAN
DDC2DETQuery(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucQueryBuffer
)
//
// DESCRIPTION:
// Reads the partial EDID structure from the monitor using DDC.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucQueryBuffer Buffer where information will be stored.
//
// RETURN VALUE:
// TRUE DDC read OK.
// FALSE DDC read failed.
//
{
DDC2_QUERY_CONTEXT ddcQueryContext;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucQueryBuffer, "lpucQueryBuffer is NULL!");
ddcQueryContext.hGDO = hGDO;
ddcQueryContext.hDDL = hDDL;
ddcQueryContext.lpucQueryBuffer = lpucQueryBuffer;
//
// Use a low priority on our critical section because it is long enough to risk problems if we disable interrupts.
//
#ifndef CE_BUILD
return SYNCEXECUTION(hDDL, VpLowPriority, DDC2DETQueryCallback, (LPVOID)&ddcQueryContext);
#else
return FALSE; // DDC not supported on WinCE
#endif
} // DDC2DETQuery()
// EPR 32286
BOOLEAN
DDC2DETQueryCallback(
LPDDC2_QUERY_CONTEXT lpDdcQueryContext
)
//
// DESCRIPTION:
// Allowes EDID to be read through VideoPortSynchronizeExecution().
//
// PARAMETERS:
// pDdcQueryContext Points to query context.
//
// RETURN VALUE:
// TRUE DDC read OK.
// FALSE DDC read failed.
//
// NOTE:
// Some monitors are extremely sensitive to timing during a DDC query. Adding debug print statements within
// the query routine will throw off the timing enough to confuse the monitor, causing the DDC query to fail.
//
{
LPGDO_RAGE_CRT hGDORageCRT;
DALASSERT(NULL != lpDdcQueryContext, "pDdcQueryContext is NULL!");
DALASSERT(NULL != lpDdcQueryContext->hDDL, "hDDL is NULL!");
hGDORageCRT = (LPGDO_RAGE_CRT)lpDdcQueryContext->hGDO;
// EPR 40230
// Initialize GPIO_15 and GPIO_16
// Ensure CRTC_ON bit is set.
//
vInitializeLT_GIO( lpDdcQueryContext->hGDO );
vEnsureCRTON( lpDdcQueryContext->hGDO );
//
// Initialize SDA and SCL lines to default state of released high (input).
// Drive SCL low for 15ms to switch DDC2-capable monitor to DDC2 mode.
//
DDC2_I2CWriteDataLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
1);
DDC2_I2CWriteClockLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
0);
DELAY_MILLISECONDS(15);
DDC2_I2CWriteClockLine(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
1);
if (DDC2_I2CWaitForClockLineHigh(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_QueryCallback: Can't switch to DDC2"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
//
// Tell the monitor that we want to talk to it and give the address we want to start with.
//
lpDdcQueryContext->lpucQueryBuffer[0] = (UCHAR)DDC2_I2C_MONITOR_ADDRESS_WRITE;
lpDdcQueryContext->lpucQueryBuffer[1] = (UCHAR)DDC2_I2C_EDID_START_ADDRESS;
lpDdcQueryContext->ulLength = 21;
DELAY_MILLISECONDS(10);
if (DDC2_I2CWrite(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
lpDdcQueryContext->lpucQueryBuffer,
2) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2DETQueryCallback: Can't write - DDC2 not supported"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
//
// Read partial EDID from the monitor.
//
ZEROMEMORY(lpDdcQueryContext->lpucQueryBuffer, lpDdcQueryContext->ulLength);
if (DDC2_I2CRead(lpDdcQueryContext->hGDO,
lpDdcQueryContext->hDDL,
lpDdcQueryContext->lpucQueryBuffer,
lpDdcQueryContext->ulLength) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2DETQueryCallback: Can't read - DDC2 not supported"));
vRestoreCRTON( lpDdcQueryContext->hGDO );
return FALSE;
}
vRestoreCRTON( lpDdcQueryContext->hGDO );
return TRUE;
} // DDC2DETQueryCallback()
BOOLEAN
DDC2_I2CStart(
HGDO hGDO,
HDDL hDDL
)
//
// DESCRIPTION:
// Starts I2C communication.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
//
// RETURN VALUE:
// TRUE OK.
// FALSE Failed.
//
{
ULONG ulRetry;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
//
// The I2C communications start signal is a SDA high->low while the SCL is high.
//
for (ulRetry = 0; ulRetry <= DDC2_I2C_START_RETRIES; ulRetry++)
{
DDC2_I2CWriteDataLine(hGDO, hDDL, 1); // Set SDA high
DDC2_I2C_DELAY();
if (DDC2_I2CReadDataLine(hGDO, hDDL) == FALSE) // SDA didn't take - retry
continue;
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -