|
|
//***************************************************************************
// Module Name: s3ddc.c
//
// Description: This module checks for a DDC monitor, and returns the
// 128 bytes of EDID table if found.
//
// Notes: The routine, DdcSetupRefresh, keeps track of resolution
// changes in the registry. On a resolution change,
// DdcSetupRefresh will select the optimal refresh rate. If
// there is NOT any change in the resolution, the user can
// select any refresh rate, as long as the monitor and
// driver can support it.
//
// Copyright (c) 1996 S3, Inc.
//
//***************************************************************************
//@@BEGIN_S3MSINTERNAL
//
// Revision History:
//
// $Log: Q:/SOFTDEV/VCS/NT/MINIPORT/s3ddc.c_v $
//
// Rev 1.13 04 Feb 1997 23:40:52 kkarnos
//Added BEGIN/END S3MSINTERNAL blocks.
//
// Rev 1.12 30 Jan 1997 14:56:24 bryhti
//Fixed the refresh frequency calculation in the Detailed Timing section
//of DdcMaxRefresh - was causing problems in NT 3.51.
//
// Rev 1.11 30 Jan 1997 09:47:36 bryhti
//Fixed the "for" loop count for Standard Timings in DdcMaxRefresh.
//
// Rev 1.10 16 Jan 1997 09:21:28 bryhti
//Added CheckDDCType routine to return monitor DDC type.
//
// Rev 1.9 11 Dec 1996 10:24:38 kkarnos
//
//Fix Set_VSYNC.
//
// Rev 1.8 10 Dec 1996 16:45:42 kkarnos
//Just added a comment to explain the source of some odd 764 code (EKL input)
//
// Rev 1.7 10 Dec 1996 16:37:08 kkarnos
//Use register and register bit defines. Correct assignment of SET VSYNC bit
//
// Rev 1.6 02 Dec 1996 07:46:16 bryhti
//
//Moved GetDdcInformation () prototype to S3.H. Added code to
//DdcMaxRefresh () to also check the Detailed Timing Descriptions.
//
// Rev 1.5 13 Nov 1996 10:14:08 bryhti
//Major cleanup/rewrite to get DDC1 and DDC2 support on M65. Also got DDC1
//support working on 765.
//
// Rev 1.4 02 Oct 1996 13:56:42 elau
//765 and new chips support DDC; the newer chip must have a serial port at FF20
//
// Rev 1.3 22 Aug 1996 11:44:40 elau
//Change int to ULONG to remove warning
//
// Rev 1.2 18 Aug 1996 16:30:42 elau
//Use HW default setting for DDC if supports
//
// Rev 1.1 24 Jul 1996 15:37:42 elau
//DDC support for 764
//
// Rev 1.0 12 Jul 1996 11:52:36 elau
//Initial revision.
//
//@@END_S3MSINTERNAL
//***************************************************************************
#include "s3.h"
#include "cmdcnst.h"
#include "s3ddc.h"
#define MMFF20 (PVOID) ((ULONG)(HwDeviceExtension->MmIoBase) + SERIAL_PORT_MM)
#define NO_FLAGS 0
#define VERIFY_CHECKSUM 1
//
// Function Prototypes
//
VOID I2C_Out (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData); VOID I2C_Setup (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID I2C_StartService (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID I2C_StopService (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID I2C_BitWrite (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData); VOID I2C_AckWrite (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID I2C_NackWrite (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR I2C_ByteWrite (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData); UCHAR I2C_BitRead (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR I2C_ByteRead (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR I2C_Data_Request (PHW_DEVICE_EXTENSION, UCHAR, long, long, UCHAR *);
VOID Wait_For_Active (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID Set_Vsync (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucFlag); VOID Provide_Fake_VSYNC (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR Read_EDID_Byte (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID Disable_DAC_Video (PHW_DEVICE_EXTENSION HwDeviceExtension); VOID Enable_DAC_Video (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR Read_EDID_Bit (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR Read_EDID_Byte (PHW_DEVICE_EXTENSION HwDeviceExtension);
UCHAR Sync_EDID_Header (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR EDID_Buffer_Xfer (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR* pBuffer);
UCHAR Check_DDC1_Monitor (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR Configure_Chip_DDC_Caps (PHW_DEVICE_EXTENSION HwDeviceExtension); UCHAR GetDdcInformation (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR* pBuffer);
/****************************************************************
; I2C_Out ; ; Controls the individual toggling of bits in MMFF20 to produce ; clock and data pulses, and in the end provides a delay. ; ; MMIO FF20h is defined as follows: ; ; ... 3 2 1 0 SCW = CLK Write ; --------|---|---|---|---| SDW = DATA Write ; ...|SDR|SCR|SDW|SCW| SCR = CLK Read ; ------------------------- SDR = DATA Read ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; UCHAR ucData ; Bit 7:2 = 0 ; Bit 1 = SDA ; Bit 0 = SCL ; Output: ; ;****************************************************************/
VOID I2C_Out (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData) {
UCHAR ucPortData; unsigned int uCount;
//
// read the current value, clear the clock and data bits, and add
// the new clock and data values
//
ucPortData = (VideoPortReadRegisterUchar (MMFF20) & 0xFC) | ucData;
VideoPortWriteRegisterUchar (MMFF20, ucPortData);
//
// if we set the clock high, wait for target to set clock high
//
if (ucData & 0x01) { uCount = 2000; do { --uCount; ucPortData = VideoPortReadRegisterUchar (MMFF20) & 0x04;
} while ( !ucPortData && uCount ); }
VideoPortStallExecution(5); }
/****************************************************************
; I2C_Setup ; ; Allow one very long low clock pulse so that monitor has time ; to switch to DDC2 mode. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;****************************************************************/
VOID I2C_Setup (PHW_DEVICE_EXTENSION HwDeviceExtension) { //
// CLK=low, DATA=high
//
I2C_Out (HwDeviceExtension, 0x02);
Wait_For_Active (HwDeviceExtension); Wait_For_Active (HwDeviceExtension);
//
// CLK=high, DATA=high
//
I2C_Out (HwDeviceExtension, 0x03);
Wait_For_Active (HwDeviceExtension); Wait_For_Active (HwDeviceExtension);
}
/****************************************************************
; I2C_StartService ; ; Provide start sequence for talking to I2C bus. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;****************************************************************/
VOID I2C_StartService (PHW_DEVICE_EXTENSION HwDeviceExtension) { //
// CLK=low, DATA=high
//
I2C_Out (HwDeviceExtension, 0x02);
//
// CLK=high, DATA=high
//
I2C_Out (HwDeviceExtension, 0x03);
//
// CLK=high, DATA=low
//
I2C_Out (HwDeviceExtension, 0x01);
//
// CLK=low, DATA=low
//
I2C_Out (HwDeviceExtension, 0x00);
}
/****************************************************************
; I2C_StopService ; ; Provide stop sequence to the I2C bus. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;***************************************************************/
VOID I2C_StopService (PHW_DEVICE_EXTENSION HwDeviceExtension) { //
// CLK=low, DATA=low
//
I2C_Out (HwDeviceExtension, 0x00);
//
// CLK=high, DATA=low
//
I2C_Out (HwDeviceExtension, 0x01);
//
// CLK=high, DATA=high
//
I2C_Out (HwDeviceExtension, 0x03);
//
// CLK=low, DATA=high
//
I2C_Out (HwDeviceExtension, 0x02); }
/****************************************************************
; I2C_BitWrite ; ; Writes one SDA bit to the I2C bus. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; Bit 1 of ucData = Bit to be written. ; ; Output: ; ;***************************************************************/
VOID I2C_BitWrite (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData) {
//
// save valid data bit
//
ucData &= 0x02;
//
// CLK=low, DATA=xxxx
//
I2C_Out (HwDeviceExtension, ucData);
//
// CLK=high, DATA=xxxx
//
I2C_Out (HwDeviceExtension, (UCHAR) (ucData | 0x01));
//
// CLK=low, DATA=xxxx
//
I2C_Out(HwDeviceExtension, ucData);
}
/****************************************************************
; I2C_ByteWrite ; ; Output a byte of information to the Display. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ucData = Byte to be written. ; ; Output: ; TRUE - write successfully ; FALSE - write failure ; ;***************************************************************/
UCHAR I2C_ByteWrite (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData) { UCHAR uOutData; int i;
uOutData = ucData;
//
// send MSB first
//
for (i=6; i >= 0; i--) { //
// move data bit to bit 1
//
uOutData = (ucData >> i); I2C_BitWrite (HwDeviceExtension, uOutData); }
//
// now send LSB
//
uOutData = (ucData << 1); I2C_BitWrite (HwDeviceExtension, uOutData);
//
// float the data line high for ACK
//
I2C_BitWrite (HwDeviceExtension, 2); return (TRUE); }
/****************************************************************
; I2C_AckWrite ; ; Send Acknowledgement when reading info. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;***************************************************************/
VOID I2C_AckWrite (PHW_DEVICE_EXTENSION HwDeviceExtension) { I2C_BitWrite (HwDeviceExtension, 0); }
/****************************************************************
; I2C_NackWrite ; ; Send Not ACKnowledgement when reading information. ; A NACK is DATA high during one clock pulse. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;***************************************************************/
VOID I2C_NackWrite (PHW_DEVICE_EXTENSION HwDeviceExtension) {
I2C_BitWrite (HwDeviceExtension, 02); }
/****************************************************************
; I2C_BitRead ; ; Reads in 1 bit from SDA via the GIP. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; Bit 0 of return value contains bit read ; ;***************************************************************/
UCHAR I2C_BitRead (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucRetval;
//
// CLK=low, DATA=high
//
I2C_Out (HwDeviceExtension, 0x02);
//
// CLK=high, DATA=high
//
I2C_Out (HwDeviceExtension, 0x03);
//
// now read in the data bit
//
ucRetval = (VideoPortReadRegisterUchar (MMFF20) & 0x08) >> 3;
//
// CLK=low, DATA=high
//
I2C_Out (HwDeviceExtension, 0x02);
return (ucRetval); }
/****************************************************************
; I2C_ByteRead ; ; Read a byte of information from the Display ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; return value is the byte read ; ;***************************************************************/
UCHAR I2C_ByteRead (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucRetval; int i;
ucRetval = 0; for (i=0; i < 8; i++) { ucRetval <<= 1; ucRetval |= I2C_BitRead (HwDeviceExtension); }
return (ucRetval); }
/****************************************************************
; I2C_DATA_Request ; ; Setup Display to query EDID or VDIF information depending ; upon the offset given. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ucWriteAddr Write Address of info ; lLength Length to read, ; lFlags VERIFY_CHECKSUM ; pBuffer pointer to buffer to receive data ; ; Output: ; TRUE successful read ; FALSE read failure or bad checksum ; ;****************************************************************/
UCHAR I2C_Data_Request ( PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucWriteAddr, long lLength, long lFlags, UCHAR *pBuffer ) { UCHAR ucData; UCHAR ucCheckSum = 0; long lCount; I2C_StartService (HwDeviceExtension); I2C_ByteWrite (HwDeviceExtension, 0xA0); //Send Device Address + write
I2C_ByteWrite (HwDeviceExtension, ucWriteAddr); //Send Write Address
I2C_StartService (HwDeviceExtension); I2C_ByteWrite (HwDeviceExtension, 0xA1); //Send Device Address + read
for (lCount = 0; lCount < lLength - 1; lCount++) { ucData= I2C_ByteRead (HwDeviceExtension); I2C_AckWrite (HwDeviceExtension); *pBuffer++ = ucData; ucCheckSum += ucData; }
ucData= I2C_ByteRead (HwDeviceExtension); I2C_NackWrite (HwDeviceExtension); *pBuffer = ucData; ucCheckSum += ucData; I2C_StopService (HwDeviceExtension);
if (lFlags & VERIFY_CHECKSUM) { if (ucCheckSum) { return (FALSE); // bad checksum
} }
return TRUE;
}
/****************************************************************
; GetDdcInformation ; ; Get 128 bytes EDID information if the monitor supports it. ; The caller is responsible for allocating the memory. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; Buffer to receive information ; ; Output: ; TRUE successful ; FALSE cannot get DdcInformation ; ;***************************************************************/
UCHAR GetDdcInformation (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR* pBuffer) { UCHAR ucOldCr40; UCHAR ucOldCr53; UCHAR ucOldCr55; UCHAR ucOldCr5C; UCHAR ucOldSr0D; UCHAR ucOldSr08; UCHAR ucOldMMFF20; UCHAR ucData; UCHAR ucRetval;
//
// unlock the Sequencer registers
//
VideoPortWritePortUchar (SEQ_ADDRESS_REG, UNLOCK_SEQREG); ucOldSr08 = ucData = VideoPortReadPortUchar (SEQ_DATA_REG); ucData = UNLOCK_SEQ; VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, SRD_SEQREG); ucOldSr0D = ucData = VideoPortReadPortUchar (SEQ_DATA_REG); ucData &= DISAB_FEATURE_BITS; // Disable feature connector
VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
//
// Enable access to the enhanced registers
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, SYS_CONFIG_S3EXTREG); ucOldCr40 = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= ENABLE_ENH_REG_ACCESS; VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// Enable MMIO
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_MEM_CTRL1_S3EXTREG); ucOldCr53 = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= (ENABLE_OLDMMIO | ENABLE_NEWMMIO); VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// GOP_1:0=00b, select MUX channel 0
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, GENERAL_OUT_S3EXTREG); ucOldCr5C = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= 0x03; VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// enable general input port
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_DAC_S3EXTREG); ucOldCr55 = VideoPortReadPortUchar (CRT_DATA_REG);
//
// the 764 doesn't support MMFF20
//
// enable the General Input Port
//
if (HwDeviceExtension->SubTypeID == SUBTYPE_764) { VideoPortWritePortUchar (CRT_DATA_REG, (UCHAR) (ucOldCr55 | ENABLE_GEN_INPORT_READ)); } else { //
// enable the serial port
//
ucOldMMFF20 = VideoPortReadRegisterUchar (MMFF20); VideoPortWriteRegisterUchar (MMFF20, 0x13); }
//
// determine DDC capabilities and branch accordingly
//
switch ( Configure_Chip_DDC_Caps (HwDeviceExtension) ) { case DDC2: I2C_Setup (HwDeviceExtension); ucRetval = I2C_Data_Request ( HwDeviceExtension, 0, // address offset
128, // read 128 bytes
VERIFY_CHECKSUM, // verify checksum
pBuffer); // buffer to put data
break;
case DDC1: Disable_DAC_Video (HwDeviceExtension);
//
// first try to sync with the EDID header
//
if (ucRetval = Sync_EDID_Header (HwDeviceExtension)) { //
// now read in the remainder of the information
//
ucRetval = EDID_Buffer_Xfer (HwDeviceExtension, pBuffer); } Enable_DAC_Video (HwDeviceExtension); break;
default: ucRetval = FALSE; // failure
break;
}
//
// restore the original register values
//
if (HwDeviceExtension->SubTypeID != SUBTYPE_764) { VideoPortWriteRegisterUchar (MMFF20, ucOldMMFF20); }
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_DAC_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr55);
VideoPortWritePortUchar (CRT_ADDRESS_REG, GENERAL_OUT_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr5C);
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_MEM_CTRL1_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr53);
VideoPortWritePortUchar (CRT_ADDRESS_REG, SYS_CONFIG_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr40);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, SRD_SEQREG); VideoPortWritePortUchar (SEQ_DATA_REG, ucOldSr0D);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, UNLOCK_SEQREG); VideoPortWritePortUchar (SEQ_DATA_REG, ucOldSr08);
return (ucRetval);
}
/****************************************************************
; Wait_For_Active ; ; Use two loop method to find VSYNC then return just after the ; falling edge. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; ;***************************************************************/
VOID Wait_For_Active (PHW_DEVICE_EXTENSION HwDeviceExtension) { PUCHAR InStatPort = SYSTEM_CONTROL_REG;
while ((VideoPortReadPortUchar (InStatPort) & VSYNC_ACTIVE) != 0) ; while ((VideoPortReadPortUchar (InStatPort) & VSYNC_ACTIVE) == 0) ; }
/****************************************************************
; Set_VSYNC ; ; Read the current polarity of the sync, then toggle it on ; if ucFlag=1, or off if ucFlag=0. ; ; Input: ; using Seq. registers PHW_DEVICE_EXTENSION ; ucFlag - see above comment ; ; Output: ; ;****************************************************************/
VOID Set_Vsync (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucFlag) {
UCHAR ucData;
//
// read Sequencer Register D and clear VSYNC bits
//
VideoPortWritePortUchar (SEQ_ADDRESS_REG, SRD_SEQREG); ucData = VideoPortReadPortUchar (SEQ_DATA_REG) & CLEAR_VSYNC;
//
// set VSYNC per the input flag
//
if (ucFlag) ucData = ((ucData & CLEAR_VSYNC) | SET_VSYNC1); else ucData = ((ucData & CLEAR_VSYNC) | SET_VSYNC0);
VideoPortWritePortUchar (SEQ_DATA_REG, ucData); }
/****************************************************************
; Provide_Fake_VSYNC ; ; Use loop delays to create a fake VSYNC signal. (~14.9KHz) ; ; Input: ; using Seq. registers PHW_DEVICE_EXTENSION ; ; Output: ; ;***************************************************************/
VOID Provide_Fake_VSYNC (PHW_DEVICE_EXTENSION HwDeviceExtension) { int i;
Set_Vsync (HwDeviceExtension, 0x01); // Turn on VSYNC
VideoPortStallExecution(5);
Set_Vsync (HwDeviceExtension, 0x00); // Turn off VSYNC
VideoPortStallExecution(5);
}
/****************************************************************
; Disable_DAC_Video ; ; Disable the DAC video driving BLANK active high. This is ; done by setting bit D5 of sequencer register 01. ;****************************************************************/
VOID Disable_DAC_Video (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucIndex; UCHAR ucData;
ucIndex = VideoPortReadPortUchar (SEQ_ADDRESS_REG);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, CLK_MODE_SEQREG);
//
// set screen off bit
//
ucData = VideoPortReadPortUchar (SEQ_DATA_REG) | SCREEN_OFF_BIT;
VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
//
// restore old index value
//
VideoPortWritePortUchar (SEQ_ADDRESS_REG, ucIndex);
}
/****************************************************************
; Disable_DAC_Video ; ; Enable the DAC video by clearing bit D5 in sequencer register 01 ;***************************************************************/
VOID Enable_DAC_Video (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucIndex; UCHAR ucData;
ucIndex = VideoPortReadPortUchar (SEQ_ADDRESS_REG);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, CLK_MODE_SEQREG);
//
// clear screen off bit
//
ucData = VideoPortReadPortUchar (SEQ_DATA_REG) & (~SCREEN_OFF_BIT);
VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
//
// restore old Index value
//
VideoPortWritePortUchar (SEQ_ADDRESS_REG, ucIndex);
}
/****************************************************************
; Read_EDID_Bit: ; ; Read the next DDC1 EDID data bit ; ; Inputs: ; PHW_DEVICE_EXTENSION HwDeviceExtension ; ; Return: ; UCHAR ucData - data in bit 0 ; ;***************************************************************/
UCHAR Read_EDID_Bit (PHW_DEVICE_EXTENSION HwDeviceExtension)
{ switch (HwDeviceExtension->SubTypeID) { case SUBTYPE_764: return (VideoPortReadPortUchar (DAC_ADDRESS_WRITE_PORT) & 1); break;
default: return ((VideoPortReadRegisterUchar (MMFF20) & 8) >> 3); break; }
}
/****************************************************************
; Read_EDID_Byte ; ; Reads eight bits from the EDID string ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; return byte value ; ;****************************************************************/
UCHAR Read_EDID_Byte (PHW_DEVICE_EXTENSION HwDeviceExtension) { long i; UCHAR ucRetData;
ucRetData = 0; for (i=0; i < 8; i++) { ucRetData <<= 1; Provide_Fake_VSYNC (HwDeviceExtension); ucRetData |= Read_EDID_Bit (HwDeviceExtension); }
return (ucRetData); }
/****************************************************************
; Sync_EDID_Header ; ; Find and sync to the header - 00 FF FF FF FF FF FF 00 ; ; Inputs: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Outputs: ; TRUE = Header Found ; FALSE = Header NOT Found ; ;***************************************************************/
UCHAR Sync_EDID_Header (PHW_DEVICE_EXTENSION HwDeviceExtension) { long lBitCount; long lEndCount; UCHAR uInSync; UCHAR ucEdidData;
//
// there are 8 * 128 bits total, but we could start reading near
// the end of the header and realize the error after starting into
// the beginning of the header and have to read the entire header
// again, so we will try reading up to 144 bytes for safety
//
// the header is 00 FF FF FF FF FF FF 00
//
lBitCount = 0; // init bit counter
do { uInSync = TRUE; // assume found header
//
// looking for 00
// checking first bit
//
for (lEndCount = lBitCount + 8; lBitCount < lEndCount; lBitCount++) { Provide_Fake_VSYNC (HwDeviceExtension); ucEdidData = Read_EDID_Bit (HwDeviceExtension); if (ucEdidData == 1) { uInSync = FALSE; break; } }
if (!uInSync) continue; // start all over
//
// send ACK
//
Provide_Fake_VSYNC (HwDeviceExtension);
//
// looking for FF FF FF FF FF FF
// 8 data bits
// 1 bit of acknowledgement
//
for (lEndCount = lBitCount + 6 * 8; lBitCount < lEndCount; lBitCount++) { Provide_Fake_VSYNC (HwDeviceExtension); ucEdidData = Read_EDID_Bit (HwDeviceExtension);
if (ucEdidData == 0) { uInSync = FALSE; break; }
//
// send an ACK if we have read 8 bits
//
if (!((lEndCount - lBitCount + 1) % 8)) { Provide_Fake_VSYNC (HwDeviceExtension); }
} if (!uInSync) continue; // start all over
//
// now looking for last 00 of header
//
for (lEndCount = lBitCount + 8; lBitCount < lEndCount; lBitCount++) { Provide_Fake_VSYNC (HwDeviceExtension); ucEdidData = Read_EDID_Bit (HwDeviceExtension);
if (ucEdidData == 1) { uInSync = FALSE; break; } }
if(!uInSync) continue; // start all over
//
// Acknowledgment
//
Provide_Fake_VSYNC (HwDeviceExtension);
} while ( (!uInSync) && (lBitCount < (8 * 144)) );
return (uInSync); }
/****************************************************************
; EDID_Buffer_Xfer ; ; Transfer all EDID data to pBuffer. Caller must allocate enough ; memory to receive 128 bytes. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; Pointer to receive buffer ; ; Output: ; TRUE data in buffer & checksum is correct ; FALSE error or bad checksum ; ;****************************************************************/
UCHAR EDID_Buffer_Xfer (PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR* pBuffer) { UCHAR ucChecksum = 0x0FA; UCHAR ucEdidData; unsigned int uCount;
//
// put the 8 header bytes in the buffer
//
*pBuffer = 0; for (uCount = 1; uCount < 7; uCount++) *(pBuffer+uCount) = 0xFF;
*(pBuffer+uCount) = 0x00;
for (uCount = 8; uCount < 128; uCount++) { ucEdidData = Read_EDID_Byte (HwDeviceExtension);
//
// send Acknowledgment
// add data to buffer
// add data to checksum
//
Provide_Fake_VSYNC (HwDeviceExtension); *(pBuffer+uCount) = ucEdidData; ucChecksum += ucEdidData; }
if (!ucChecksum) { return (TRUE); // checksum is OK
}
return (FALSE); // checksum is NOT
}
/****************************************************************
; Check_DDC1_Monitor ; ; Check for a DDC1 monitor using current vsync. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; TRUE possible DDC1 monitor ; FALSE no EDID data detected on input port ; ;****************************************************************/
UCHAR Check_DDC1_Monitor (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucSaveOldData; UCHAR ucData; UCHAR ucGD0; unsigned int uCount; UCHAR ucDDC1;
//
// assume not DDC1
//
ucDDC1 = FALSE; switch (HwDeviceExtension->SubTypeID) { //
// use reads from 3C8 on the 764 (undocumented, but this use
// of the DAC register comes from the 764 BIOS source code).
//
case SUBTYPE_764: ucSaveOldData = VideoPortReadPortUchar (MISC_OUTPUT_REG_READ);
//
// Bit 7 = 0 Positive VSYNC
//
VideoPortWritePortUchar (MISC_OUTPUT_REG_WRITE, (UCHAR) (ucSaveOldData & SEL_POS_VSYNC)); Wait_For_Active (HwDeviceExtension);
ucData = VideoPortReadPortUchar (DAC_ADDRESS_WRITE_PORT);
//
// Another read for VL systems. (Data left on the GD/SD lines)
//
ucGD0 = VideoPortReadPortUchar (DAC_ADDRESS_WRITE_PORT) & 0x01;
//
// read up to 350 bits looking for the data to toggle, indicating
// DDC1 data is being sent
//
for (uCount = 0; uCount < 350; uCount++) { Wait_For_Active (HwDeviceExtension); ucData = VideoPortReadPortUchar (DAC_ADDRESS_WRITE_PORT) & 0x01; if (ucData != ucGD0) { //
// data line toggled, assume DDC1 data is being sent
//
ucDDC1 = TRUE; break; } }
//
// restore old value
//
VideoPortWritePortUchar (MISC_OUTPUT_REG_WRITE, ucSaveOldData); break;
//
// else use MMFF20 on the other chips
//
default: Disable_DAC_Video (HwDeviceExtension); Provide_Fake_VSYNC (HwDeviceExtension); ucGD0 = VideoPortReadRegisterUchar (MMFF20) & 8;
for (uCount = 0; uCount < 350; uCount++) { Provide_Fake_VSYNC (HwDeviceExtension); ucData = VideoPortReadRegisterUchar (MMFF20) & 8; if (ucData != ucGD0) { //
// data line toggled, assume DDC1 data is being sent
//
ucDDC1 = TRUE; break; } } Enable_DAC_Video (HwDeviceExtension); break;
}
return (ucDDC1);
} /****************************************************************
; Configure_Chip_DDC_Caps ; ; Determine DDC capabilities of display. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; NO_DDC ; DDC1: Support DDC1 ; DDC2: Support DDC2 ; ;****************************************************************/
UCHAR Configure_Chip_DDC_Caps (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucBuffer[2];
//
// we will only use DDC1 on 764
//
if (HwDeviceExtension->SubTypeID != SUBTYPE_764) { //
// first check if DDC2 capable
//
I2C_Setup (HwDeviceExtension); I2C_Data_Request ( HwDeviceExtension, 0, // address offset
2, // look at first 2 bytes
NO_FLAGS, // don't verify checksum
ucBuffer ); // buffer to place data
//
// check if the first 2 bytes of the EDID header look correct
//
if ( (ucBuffer [0] == 0) && (ucBuffer [1] == 0xFF) ) { return (DDC2); // assume DDC2 capable
} }
//
// try DDC1
//
if (Check_DDC1_Monitor (HwDeviceExtension)) { return (DDC1); }
return (NO_DDC); }
//---------------------------------------------------------------------------
ULONG DdcMaxRefresh(ULONG uXresolution, UCHAR * pEdid) { ULONG uMaxFreq = 0; ULONG uEdidRes; ULONG uEdidFreq; ULONG HorRes, VertRes; ULONG i, Index;
//
// Detailed timing
//
for (i = 0; i < 4; ++i) // 4 Detailed Descriptions
{ Index = 54 + i * 18; if ( (pEdid [Index] == 0) && (pEdid [Index + 1] == 0) && (pEdid [Index + 2] == 0) ) { continue; // Monitor descriptor block, skip it
}
HorRes = ((ULONG) (pEdid [Index + 4] & 0xF0)) << 4; HorRes += (ULONG) pEdid [Index + 2];
if (HorRes == uXresolution) { //
// add Horizontal blanking
//
HorRes += (ULONG) pEdid [Index + 3]; HorRes += ((ULONG) (pEdid [Index + 4] & 0x0F)) << 8;
//
// now get Vertical Total (Active & Blanking)
//
VertRes = ((ULONG) (pEdid [Index + 7] & 0xF0)) << 4; VertRes += ((ULONG) (pEdid [Index + 7] & 0x0F)) << 8; VertRes += (ULONG) pEdid [Index + 5]; VertRes += (ULONG) pEdid [Index + 6];
uEdidFreq = (((ULONG) pEdid [Index + 1]) << 8) + ((ULONG) pEdid [Index]);
uEdidFreq = uEdidFreq * 10000 / HorRes / VertRes;
if (uEdidFreq > uMaxFreq) { uMaxFreq = uEdidFreq; } } } //
// Standard timing id.
//
for (i = 38; i < 54; i += 2) { uEdidRes = (((ULONG) pEdid[i]) + 31) * 8; if (uXresolution == uEdidRes) { uEdidFreq = (((ULONG) pEdid[i+1]) & 0x3F) + 60; if (uEdidFreq > uMaxFreq) { uMaxFreq = uEdidFreq; } } }
//
// Established timing
//
switch (uXresolution) { case 640: uEdidFreq = (ULONG)pEdid[0x23]; if (uEdidFreq & 0x020) { if (uMaxFreq < 60) { uMaxFreq = 60; } } if (uEdidFreq & 0x08) { if (uMaxFreq < 72) { uMaxFreq = 72; } } if (uEdidFreq & 0x04) { if (uMaxFreq < 75) { uMaxFreq = 75; } } break;
case 800: uEdidFreq = (ULONG)pEdid[0x23]; if (uEdidFreq & 0x02) { if (uMaxFreq < 56) { uMaxFreq = 56; } } if (uEdidFreq & 0x01) { if (uMaxFreq < 60) { uMaxFreq = 60; } }
uEdidFreq = (ULONG)pEdid[0x24]; if (uEdidFreq & 0x80) { if (uMaxFreq < 72) { uMaxFreq = 72; } } if (uEdidFreq & 0x40) { if (uMaxFreq < 75) { uMaxFreq = 75; } } break;
case 1024: uEdidFreq = (ULONG)pEdid[0x24]; if (uEdidFreq & 0x08) { if (uMaxFreq < 60) { uMaxFreq = 60; } } if (uEdidFreq & 0x04) { if (uMaxFreq < 70) { uMaxFreq = 70; } } if (uEdidFreq & 0x02) { if (uMaxFreq < 75) { uMaxFreq = 75; } } break;
case 1280: uEdidFreq = (ULONG)pEdid[0x24]; if (uEdidFreq & 0x01) { if (uMaxFreq < 75) { uMaxFreq = 75; } } break; }
return(uMaxFreq);
}
//---------------------------------------------------------------------------
ULONG DdcRefresh (PHW_DEVICE_EXTENSION hwDeviceExtension, ULONG uXResolution) {
ULONG lRefresh = 0; char szBuffer[200];
if (GetDdcInformation (hwDeviceExtension, szBuffer)) { lRefresh = DdcMaxRefresh (uXResolution, szBuffer); }
return lRefresh; }
/****************************************************************
; CheckDDCType ; ; Check the monitor for DDC type. ; ; Input: ; Using MMIO Base in PHW_DEVICE_EXTENSION ; ; Output: ; NO_DDC non-DDC monitor ; DDC1 DDC1 monitor ; DDC2 DDC2 monitor ; ;***************************************************************/
UCHAR CheckDDCType (PHW_DEVICE_EXTENSION HwDeviceExtension) { UCHAR ucOldCr40; UCHAR ucOldCr53; UCHAR ucOldCr55; UCHAR ucOldCr5C; UCHAR ucOldSr0D; UCHAR ucOldSr08; UCHAR ucOldMMFF20; UCHAR ucData; UCHAR ucRetval;
//
// unlock the Sequencer registers
//
VideoPortWritePortUchar (SEQ_ADDRESS_REG, UNLOCK_SEQREG); ucOldSr08 = ucData = VideoPortReadPortUchar (SEQ_DATA_REG); ucData = UNLOCK_SEQ; VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, SRD_SEQREG); ucOldSr0D = ucData = VideoPortReadPortUchar (SEQ_DATA_REG); ucData &= DISAB_FEATURE_BITS; // Disable feature connector
VideoPortWritePortUchar (SEQ_DATA_REG, ucData);
//
// Enable access to the enhanced registers
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, SYS_CONFIG_S3EXTREG); ucOldCr40 = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= ENABLE_ENH_REG_ACCESS; VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// Enable MMIO
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_MEM_CTRL1_S3EXTREG); ucOldCr53 = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= (ENABLE_OLDMMIO | ENABLE_NEWMMIO); VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// GOP_1:0=00b, select MUX channel 0
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, GENERAL_OUT_S3EXTREG); ucOldCr5C = ucData = VideoPortReadPortUchar (CRT_DATA_REG); ucData |= 0x03; VideoPortWritePortUchar (CRT_DATA_REG, ucData);
//
// enable general input port
//
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_DAC_S3EXTREG); ucOldCr55 = VideoPortReadPortUchar (CRT_DATA_REG);
//
// the 764 doesn't support MMFF20
//
// enable the General Input Port
//
if (HwDeviceExtension->SubTypeID == SUBTYPE_764) { VideoPortWritePortUchar (CRT_DATA_REG, (UCHAR) (ucOldCr55 | ENABLE_GEN_INPORT_READ)); } else { //
// enable the serial port
//
ucOldMMFF20 = VideoPortReadRegisterUchar (MMFF20); VideoPortWriteRegisterUchar (MMFF20, 0x13); }
//
// determine DDC capabilities and branch accordingly
//
ucRetval = Configure_Chip_DDC_Caps (HwDeviceExtension);
//
// restore the original register values
//
if (HwDeviceExtension->SubTypeID != SUBTYPE_764) { VideoPortWriteRegisterUchar (MMFF20, ucOldMMFF20); }
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_DAC_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr55);
VideoPortWritePortUchar (CRT_ADDRESS_REG, GENERAL_OUT_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr5C);
VideoPortWritePortUchar (CRT_ADDRESS_REG, EXT_MEM_CTRL1_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr53);
VideoPortWritePortUchar (CRT_ADDRESS_REG, SYS_CONFIG_S3EXTREG); VideoPortWritePortUchar (CRT_DATA_REG, ucOldCr40);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, SRD_SEQREG); VideoPortWritePortUchar (SEQ_DATA_REG, ucOldSr0D);
VideoPortWritePortUchar (SEQ_ADDRESS_REG, UNLOCK_SEQREG); VideoPortWritePortUchar (SEQ_DATA_REG, ucOldSr08);
return (ucRetval);
}
|