windows-nt/Source/XPSP1/NT/drivers/video/ms/cirrus/mini/ddc.c
2020-09-26 16:20:57 +08:00

247 lines
6.1 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
ddc.c
Abstract:
This module contains the code that support DDC querying..
Environment:
Kernel mode
Revision History:
--*/
#include <dderror.h>
#include <devioctl.h>
#include <miniport.h>
#include <ntddvdeo.h>
#include <video.h>
#include <cirrus.h>
VOID WriteClockLine(PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData);
VOID WriteDataLine(PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData);
BOOLEAN ReadClockLine(PHW_DEVICE_EXTENSION HwDeviceExtension);
BOOLEAN ReadDataLine(PHW_DEVICE_EXTENSION HwDeviceExtension);
VOID WaitForVsyncActive(PHW_DEVICE_EXTENSION HwDeviceExtension);
/****************************************************************
; DDC register
;
; Controls the individual toggling of bits in Sr8 to produce
; clock and data pulses.
;
; Sr8 is defined as follows:
;
; 7 ... 2 1 0 SCW = CLK Write
; |---|---|---|---|---| SDW = DATA Write
; |SDR ...|SCR|SDW|SCW| SCR = CLK Read
; --------------------- SDR = DATA Read
;
;****************************************************************/
#define DDC_PORT (HwDeviceExtension->IOAddress + SEQ_DATA_PORT)
#define STATUS_PORT (HwDeviceExtension->IOAddress + INPUT_STATUS_1_COLOR)
#define VSYNC_ACTIVE 0x08
VOID WriteClockLine(PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData)
{
UCHAR ucPortData;
//
// read the current value and reset the clock line.
//
ucPortData = (VideoPortReadPortUchar(DDC_PORT) & 0xFE) | ucData;
VideoPortWritePortUchar(DDC_PORT, ucPortData);
}
VOID WriteDataLine(PHW_DEVICE_EXTENSION HwDeviceExtension, UCHAR ucData)
{
UCHAR ucPortData;
//
// read the current value and reset the data line.
//
ucPortData = (VideoPortReadPortUchar(DDC_PORT) & 0xFD) | (ucData << 1);
VideoPortWritePortUchar(DDC_PORT, ucPortData);
}
BOOLEAN ReadClockLine(PHW_DEVICE_EXTENSION HwDeviceExtension)
{
UCHAR uc;
uc = VideoPortReadPortUchar(DDC_PORT);
//VideoDebugPrint((0, "Read = 0x%x\n", uc));
return ((VideoPortReadPortUchar(DDC_PORT) & 0x04) >> 2);
}
BOOLEAN ReadDataLine(PHW_DEVICE_EXTENSION HwDeviceExtension)
{
UCHAR uc;
uc = VideoPortReadPortUchar(DDC_PORT);
//VideoDebugPrint((0, "Read = 0x%x\n", uc));
return ((VideoPortReadPortUchar(DDC_PORT) & 0x80) >> 7);
}
VOID WaitForVsyncActive(PHW_DEVICE_EXTENSION HwDeviceExtension)
{
while ((VideoPortReadPortUchar(STATUS_PORT) & VSYNC_ACTIVE) != 0);
while ((VideoPortReadPortUchar(STATUS_PORT) & VSYNC_ACTIVE) == 0);
}
BOOLEAN
GetDdcInformation(
PHW_DEVICE_EXTENSION HwDeviceExtension,
PUCHAR QueryBuffer,
ULONG BufferSize)
/*++
Routine Description:
Reads the basic EDID structure from the monitor using DDC2.
Arguments:
HwDeviceExtension - Points to per-adapter device extension.
QueryBuffer - Buffer where information will be stored.
BufferSize - Size of the buffer to fill.
Return Value:
Whether the call succeeded or not.
--*/
{
UCHAR ucData;
BOOLEAN bRet = FALSE;
I2C_FNC_TABLE i2c;
ULONG i;
UCHAR OldSeqIdx;
UCHAR ucSr6;
UCHAR ucSr8;
// workaround for Cirrus HW problem (part 1/2)
static UCHAR onceQueryBuffer [512]; // EDID length is expected to be 128 or 256 bytes
static UCHAR onceReadAttempt = FALSE;
static UCHAR onceReturnedValue;
if (onceReadAttempt) {
VideoDebugPrint((1, "CIRRUS: ONCE READ => returning previously obtained data\n"));
memcpy (QueryBuffer, onceQueryBuffer, BufferSize);
return onceReturnedValue;
}
// end of the workaround (part 1/2)
OldSeqIdx = VideoPortReadPortUchar(HwDeviceExtension->IOAddress
+ SEQ_ADDRESS_PORT);
//
// Make sure the extension registers are unlocked.
//
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_ADDRESS_PORT,
0x6);
ucSr6 = VideoPortReadPortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT,
0x12);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_ADDRESS_PORT,
0x08);
ucSr8 = VideoPortReadPortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT,
(UCHAR)(ucSr8 | 0x40));
i2c.WriteClockLine = WriteClockLine;
i2c.WriteDataLine = WriteDataLine;
i2c.ReadClockLine = ReadClockLine;
i2c.ReadDataLine = ReadDataLine;
i2c.WaitVsync = WaitForVsyncActive;
i2c.Size = sizeof(I2C_FNC_TABLE);
// 5430/5440 has a problem doing DDC unless we wait for vsync first.
if (HwDeviceExtension->ChipType == CL543x && HwDeviceExtension->ChipRevision == CL5430_ID)
{
WaitForVsyncActive(HwDeviceExtension);
}
bRet = VideoPortDDCMonitorHelper(HwDeviceExtension,
&i2c,
QueryBuffer,
BufferSize);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_ADDRESS_PORT,
0x08);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT,
ucSr8);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_ADDRESS_PORT,
0x6);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_DATA_PORT,
ucSr6);
VideoPortWritePortUchar(
HwDeviceExtension->IOAddress + SEQ_ADDRESS_PORT,
OldSeqIdx);
// workaround for Cirrus HW problem (part 2/2)
onceReadAttempt = TRUE;
onceReturnedValue = bRet;
VideoDebugPrint((1, "CIRRUS: first EDID reading attempt "));
if (onceReturnedValue)
VideoDebugPrint((1, "succeeded"));
else
VideoDebugPrint((1, "failed"));
VideoDebugPrint((1, " - the result saved\n"));
memcpy (onceQueryBuffer, QueryBuffer, BufferSize);
// end of the workaround (part 1/2)
return bRet;
}