📄 ltddc.c
字号:
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CStart: SCL didn't take"));
break;
}
DDC2_I2CWriteDataLine(hGDO, hDDL, 0); // Set SDA low
DDC2_I2C_DELAY();
DDC2_I2CWriteClockLine(hGDO, hDDL, 0); // Set SCL low
DDC2_I2C_DELAY();
return TRUE;
}
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CStart: Failed"));
return FALSE;
} // DDC2_I2CStart()
BOOLEAN
DDC2_I2CStop(
HGDO hGDO,
HDDL hDDL
)
//
// DESCRIPTION:
// Stops I2C communication.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
//
// RETURN VALUE:
// TRUE OK.
// FALSE Failed.
//
{
DALASSERT(hDDL != NULL, "hDDL is NULL!");
//
// The I2C communications stop signal is a SDA low->high while the SCL is high.
//
DDC2_I2CWriteDataLine(hGDO, hDDL, 0); // Set SDA low
DDC2_I2C_DELAY();
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CStop: SCL didn't take"));
return FALSE;
}
DDC2_I2CWriteDataLine(hGDO, hDDL, 1); // Set SDA high
DDC2_I2C_DELAY();
if (DDC2_I2CReadDataLine(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CI2CStop: SDA didn't take"));
return FALSE;
}
return TRUE;
} // DDC2_I2CStop()
BOOLEAN
DDC2_I2CWrite(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucBuffer,
ULONG ulLength
)
//
// DESCRIPTION:
// Writes data to the DDC2 monitor.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucBuffer Points to data to be written.
// ulLength Number of bytes to write.
//
// RETURN VALUE:
// TRUE Write OK.
// FALSE Write failed.
//
{
ULONG ulCount;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucBuffer, "lpucBuffer is NULL!");
if (DDC2_I2CStart(hGDO, hDDL) == FALSE)
return FALSE;
for (ulCount = 0; ulCount < ulLength; ulCount++)
{
if (DDC2_I2CWriteByte(hGDO, hDDL, lpucBuffer[ulCount]) == FALSE)
{
DDC2_I2CStop(hGDO, hDDL);
return FALSE;
}
}
if (DDC2_I2CStop(hGDO, hDDL) == FALSE)
return FALSE;
return TRUE;
} // DDC2_I2CWrite()
BOOLEAN
DDC2_I2CWriteByte(
HGDO hGDO,
HDDL hDDL,
UCHAR ucByte
)
//
// DESCRIPTION:
// Sends byte over I2C channel.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// ucByte Byte to write.
//
// RETURN VALUE:
// TRUE Write OK.
// FALSE Write failed.
//
{
LONG lShift;
BOOLEAN bAck;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
//
// Bits are transmitted serially starting with the MSB.
//
for (lShift = 7; lShift >= 0; lShift--)
{
//
// Transmitt data bit.
//
DDC2_I2CWriteDataLine(hGDO, hDDL, (UCHAR)((ucByte >> lShift) & 0x01)); // Set SDA
DDC2_I2C_DELAY();
//
// After each data bit we must send high->low SCL pulse.
//
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CWriteByte: SCL didn't take"));
return FALSE;
}
DDC2_I2CWriteClockLine(hGDO, hDDL, 0); // Set SCL low
DDC2_I2C_DELAY();
}
//
// The monitor sends ACK by preventing the SDA from going high after the clock pulse we use
// to send our last data bit. If the SDA goes high after this bit, it is a NAK from the monitor.
//
DDC2_I2CWriteDataLine(hGDO, hDDL, 1); // Set SDA high
DDC2_I2C_DELAY();
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CWriteByte: SCL didn't take - ACK failed"));
return FALSE;
}
bAck = DDC2_I2CReadDataLine(hGDO, hDDL); // Read ACK bit
DDC2_I2CWriteClockLine(hGDO, hDDL, 0); // Set SCL low
DDC2_I2C_DELAY();
if (TRUE == bAck) // NAK from the monitor
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CWriteByte: NAK received"));
return FALSE;
}
return TRUE;
} // DDC2_I2CWriteByte()
BOOLEAN
DDC2_I2CRead(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucBuffer,
ULONG ulLength
)
//
// DESCRIPTION:
// Reads data from the DDC2 monitor.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucBuffer Points to storage for data.
// ulLength Number of bytes to read.
//
// RETURN VALUE:
// TRUE Read OK.
// FALSE Read failed.
//
{
ULONG ulCount;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucBuffer, "lpucBuffer is NULL!");
if (DDC2_I2CStart(hGDO, hDDL) == FALSE)
return FALSE;
//
// Tell the monitor that we want to listen to it.
//
if (DDC2_I2CWriteByte(hGDO, hDDL, DDC2_I2C_MONITOR_ADDRESS_READ) == FALSE)
{
DDC2_I2CStop(hGDO, hDDL);
return FALSE;
}
//
// On all but the last byte, we must send an ACK in order to ensure that the sending device will
// send subsequent data bytes. On the last byte, we must send a NAK so that it will shut up.
//
for (ulCount = 0; ulCount < ulLength; ulCount++)
{
if (ulLength - 1 == ulCount)
{
if (DDC2_I2CReadByte(hGDO, hDDL, lpucBuffer + ulCount, FALSE) == FALSE) // Last byte
{
DDC2_I2CStop(hGDO, hDDL);
return FALSE;
}
}
else
{
if (DDC2_I2CReadByte(hGDO, hDDL, lpucBuffer + ulCount, TRUE) == FALSE)
{
DDC2_I2CStop(hGDO, hDDL);
return FALSE;
}
}
}
if (DDC2_I2CStop(hGDO, hDDL) == FALSE)
return FALSE;
return TRUE;
} // DDC2_I2CRead()
BOOLEAN
DDC2_I2CReadByte(
HGDO hGDO,
HDDL hDDL,
LPUCHAR lpucByte,
BOOLEAN bMore
)
//
// DESCRIPTION:
// Receives byte over I2C channel.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// lpucByte Points to storage for read byte.
// bMore TRUE if we want to continue reading, FALSE otherwise.
//
// RETURN VALUE:
// TRUE Read OK - received byte in lpucByte.
// FALSE Read failed.
//
// NOTE:
// We don't have to relaese SDA high (input) before read. Before first bit is read
// we always write address byte -- write releases SDA high on exit. For each subsequent
// byte we're also OK since we release SDA high on exit from this routine.
//
{
LONG lShift;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
DALASSERT(NULL != lpucByte, "lpucByte is NULL!");
*lpucByte = 0;
//
// The data bits are read from MSB to LSB. A data bit is read while the SCL is high.
//
for (lShift = 7; lShift >= 0; lShift--)
{
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CReadByte: SCL didn't take"));
return FALSE;
}
*lpucByte |= DDC2_I2CReadDataLine(hGDO, hDDL) << lShift; // Read SDA
DDC2_I2CWriteClockLine(hGDO, hDDL, 0); // Set SCL low
DDC2_I2C_DELAY();
}
//
// Send the acknowledge bit. SDA low = ACK, SDA high = NAK.
//
if (TRUE == bMore)
DDC2_I2CWriteDataLine(hGDO, hDDL, 0); // Set SDA low - ACK
else
DDC2_I2CWriteDataLine(hGDO, hDDL, 1); // Set SDA high - NAK
DDC2_I2C_DELAY();
//
// Send a SCL high->low pulse, then release the SDA by setting it high.
//
DDC2_I2CWriteClockLine(hGDO, hDDL, 1); // Set SCL high
DDC2_I2C_DELAY();
if (DDC2_I2CWaitForClockLineHigh(hGDO, hDDL) == FALSE)
{
DALDEBUG((DALDBG_DETAIL, "DDC2_I2CReadByte: SCL didn't take - ACK failed"));
return FALSE;
}
DDC2_I2CWriteClockLine(hGDO, hDDL, 0); // Set SCL low
DDC2_I2C_DELAY();
DDC2_I2CWriteDataLine(hGDO, hDDL, 1); // Set SDA high
DDC2_I2C_DELAY();
return TRUE;
} // DDC2_I2CReadByte()
BOOLEAN
DDC2_I2CWaitForClockLineHigh(
HGDO hGDO,
HDDL hDDL
)
//
// DESCRIPTION:
// Waits till SCL goes high (SCL low period can be stretched by slow monitors).
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
//
// RETURN VALUE:
// TRUE OK - SCL high.
// FALSE SCL didn't take.
//
{
ULONG ulCount;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
for (ulCount = 0; ulCount < DDC2_I2C_SCL_READ_RETRIES; ulCount++)
{
if (DDC2_I2CReadClockLine(hGDO, hDDL) == TRUE)
return TRUE;
DDC2_I2C_DELAY();
}
return FALSE;
} // DDC2_I2CWaitForClockLineHigh
#endif // _WIN32_WINNT >= 0x500
//
// DDC through LT_GIO register for LT adapters:
// On LT Pro LT_GIO is hiding behind LCD_INDEX/LCD_DATA.
//
// SCL = Pin GPIO15
// SDA = Pin GPIO16
//
// LT_GIO + 1: Bit 5 = SCL Read
// Bit 6 = SDA Read
//
// LT_GIO + 3: Bit 5 = SCL Direction, 0 = Input, 1 = Output
// Bit 6 = SDA Direction, 0 = Input, 1 = Output
//
// NOTE:
// For DDC communication we are the I2C master and we drive the SCL line.
// Setting I2C line to input direction drives it high (releases it high - default).
// Setting I2C line to output direction drives it low (pulls it to ground).
//
//
// TODO: Verify if there is no conflict between I2C/GP_IO Interface functions and DDC Helper functions.
// If there is a chance of conflict we should use VideoPortSynchronizeExecution() callback functions for
// I2C read/write.
//
VOID
DDC2_I2CWriteClockLine(
HGDO hGDO,
HDDL hDDL,
UCHAR ucData
)
//
// DESCRIPTION:
// Sets DDC I2C clock line (SCL) low / high.
//
// PARAMETERS:
// hDDL Points to per-adapter device extension.
// ucData 0 = drive SCL low, 1 = drive SCL high.
//
{
LPGDO_RAGE_CRT hGDORageCRT;
UCHAR ucTemp;
DALASSERT(NULL != hDDL, "hDDL is NULL!");
hGDORageCRT = (LPGDO_RAGE_CRT)hGDO;
ucData &= 0x01; // Mask
//sunnyvale [vichan] port by [espiritu]
if (FAMILY_GTC == (hGDORageCRT->ulChipFamily))
{
//rage pro specific code
ucTemp = (UCHAR)(MMREADUCHAR(hGDORageCRT->lpMMR, GP_IO, 3) | (0x01 << 5));
MMWRITEUCHAR(hGDORageCRT->lpMMR, GP_IO, ucTemp, 3);
ucTemp = (MMREADUCHAR(hGDORageCRT->lpMMR, GP_IO, 1) & ~(0x01 << 5)) | (ucData<< 5);
MMWRITEUCHAR(hGDORageCRT->lpMMR, GP_IO, ucTemp, 1);
}
else
{
ucData ^= 0x01; // Invert
//
// Set the SCL enable line.
//
if( hGDORageCRT->ulChipFamily == FAMILY_LT_PRO||
hGDORageCRT->ulChipFamily == FAMILY_RAGE_MOBILITY)
{
// Set LCD_DATA register to index 7 (LCD_INDEX_LT_GIO).
MMWRITEUCHAR(hGDORageCRT->lpMMR, LCD_INDEX, LCD_INDEX_LtGio, 0);
ucTemp = (MMREADUCHAR(hGDORageCRT->lpMMR, LCD_DATA, 3) &
~(0x20))| (ucData << 5);
MMWRITEUCHAR(hGDORageCRT->lpMMR, LCD_DATA, ucTemp, 3);
}
else
{
// EPR 35356.
if (hGDORageCRT->bREADEDIDFROMDVI == TRUE)
{
//
// Rage XL: Handle SCL through DVI connector. (Same as DFP)
//
// Set LCD_DATA register to index 28 (LCD_INDEX_TestIO).
MMWRITEUCHAR(hGDORageCRT->lpMMR, LCD_INDEX, LCD_INDEX_TestIO, 0);
//
// Set the SCL enable line (Rage XL).
//
ucTemp = (MMREADUCHAR(hGDORageCRT->lpMMR, LCD_DATA, 1) &
~(0x01))| ucData;
if (ucData)
{
ucTemp &= ~(0x04);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -