reactos/win32ss/drivers/videoprt/ddc.c

228 lines
5.6 KiB
C

/*
* VideoPort driver
*
* Copyright (C) 2002, 2003, 2004 ReactOS Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "videoprt.h"
#define NDEBUG
#include <debug.h>
#define DDC_EEPROM_ADDRESS 0xA0
/* PRIVATE FUNCTIONS **********************************************************/
#define LOW 0
#define HIGH 1
#define WRITE 0
#define READ 1
#define READ_SDA() (i2c->ReadDataLine(HwDeviceExtension))
#define READ_SCL() (i2c->ReadClockLine(HwDeviceExtension))
#define WRITE_SDA(state) (i2c->WriteDataLine(HwDeviceExtension, state))
#define WRITE_SCL(state) (i2c->WriteClockLine(HwDeviceExtension, state))
static LARGE_INTEGER HalfPeriodDelay = {{0, 70}};
#define DELAY_HALF() KeDelayExecutionThread(KernelMode, FALSE, &HalfPeriodDelay)
static BOOL
I2CWrite(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Data)
{
UCHAR Bit;
BOOL Ack;
/* transmit data */
for (Bit = (1 << 7); Bit != 0; Bit >>= 1)
{
WRITE_SCL(LOW);
WRITE_SDA((Data & Bit) ? HIGH : LOW);
DELAY_HALF();
WRITE_SCL(HIGH);
DELAY_HALF();
}
/* get ack */
WRITE_SCL(LOW);
WRITE_SDA(HIGH);
DELAY_HALF();
WRITE_SCL(HIGH);
do
{
DELAY_HALF();
}
while (READ_SCL() != HIGH);
Ack = (READ_SDA() == LOW);
DELAY_HALF();
INFO_(VIDEOPRT, "I2CWrite: %s\n", Ack ? "Ack" : "Nak");
return Ack;
}
static UCHAR
I2CRead(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, BOOL Ack)
{
INT Bit = 0x80;
UCHAR Data = 0;
/* pull down SCL and release SDA */
WRITE_SCL(LOW);
WRITE_SDA(HIGH);
/* read byte */
for (Bit = (1 << 7); Bit != 0; Bit >>= 1)
{
WRITE_SCL(LOW);
DELAY_HALF();
WRITE_SCL(HIGH);
DELAY_HALF();
if (READ_SDA() == HIGH)
Data |= Bit;
}
/* send ack/nak */
WRITE_SCL(LOW);
WRITE_SDA(Ack ? LOW : HIGH);
DELAY_HALF();
WRITE_SCL(HIGH);
do
{
DELAY_HALF();
}
while (READ_SCL() != HIGH);
return Data;
}
static VOID
I2CStop(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c)
{
WRITE_SCL(LOW);
WRITE_SDA(LOW);
DELAY_HALF();
WRITE_SCL(HIGH);
DELAY_HALF();
WRITE_SDA(HIGH);
}
static BOOL
I2CStart(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Address)
{
/* make sure the bus is free */
if (READ_SDA() == LOW || READ_SCL() == LOW)
{
WARN_(VIDEOPRT, "I2CStart: Bus is not free!\n");
return FALSE;
}
/* send address */
WRITE_SDA(LOW);
DELAY_HALF();
if (!I2CWrite(HwDeviceExtension, i2c, Address))
{
/* ??release the bus?? */
I2CStop(HwDeviceExtension, i2c);
WARN_(VIDEOPRT, "I2CStart: Device not found (Address = 0x%x)\n", Address);
return FALSE;
}
INFO_(VIDEOPRT, "I2CStart: SUCCESS!\n");
return TRUE;
}
static BOOL
I2CRepStart(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Address)
{
/* setup lines for repeated start condition */
WRITE_SCL(LOW);
DELAY_HALF();
WRITE_SDA(HIGH);
DELAY_HALF();
WRITE_SCL(HIGH);
DELAY_HALF();
return I2CStart(HwDeviceExtension, i2c, Address);
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
BOOLEAN NTAPI
VideoPortDDCMonitorHelper(
PVOID HwDeviceExtension,
PVOID I2CFunctions,
PUCHAR pEdidBuffer,
ULONG EdidBufferSize
)
{
PDDC_CONTROL ddc = (PDDC_CONTROL)I2CFunctions;
PI2C_CALLBACKS i2c = &ddc->I2CCallbacks;
INT Count, i;
PUCHAR pBuffer = (PUCHAR)pEdidBuffer;
BOOL Ack;
TRACE_(VIDEOPRT, "VideoPortDDCMonitorHelper()\n");
ASSERT_IRQL_LESS_OR_EQUAL(PASSIVE_LEVEL);
if (ddc->Size != sizeof (ddc))
{
WARN_(VIDEOPRT, "ddc->Size != %d (%d)\n", sizeof (ddc), ddc->Size);
return FALSE;
}
/* select eeprom */
if (!I2CStart(HwDeviceExtension, i2c, DDC_EEPROM_ADDRESS | WRITE))
return FALSE;
/* set address */
if (!I2CWrite(HwDeviceExtension, i2c, 0x00))
return FALSE;
/* change into read mode */
if (!I2CRepStart(HwDeviceExtension, i2c, DDC_EEPROM_ADDRESS | READ))
return FALSE;
/* read eeprom */
RtlZeroMemory(pEdidBuffer, EdidBufferSize);
Count = min(128, EdidBufferSize);
for (i = 0; i < Count; i++)
{
Ack = ((i + 1) < Count);
pBuffer[i] = I2CRead(HwDeviceExtension, i2c, Ack);
}
I2CStop(HwDeviceExtension, i2c);
/* check EDID header */
if (pBuffer[0] != 0x00 || pBuffer[1] != 0xff ||
pBuffer[2] != 0xff || pBuffer[3] != 0xff ||
pBuffer[4] != 0xff || pBuffer[5] != 0xff ||
pBuffer[6] != 0xff || pBuffer[7] != 0x00)
{
WARN_(VIDEOPRT, "VideoPortDDCMonitorHelper(): Invalid EDID header!\n");
return FALSE;
}
INFO_(VIDEOPRT, "VideoPortDDCMonitorHelper(): EDID version %d rev. %d\n", pBuffer[18], pBuffer[19]);
INFO_(VIDEOPRT, "VideoPortDDCMonitorHelper() - SUCCESS!\n");
return TRUE;
}