471 lines
12 KiB
C
471 lines
12 KiB
C
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atimach64i2c.c,v 1.1 2003/07/24 22:08:28 tsi Exp $ */
|
|
/*
|
|
* Copyright 2003 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of Marc Aurele La France not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. Marc Aurele La France makes no representations
|
|
* about the suitability of this software for any purpose. It is provided
|
|
* "as-is" without express or implied warranty.
|
|
*
|
|
* MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
|
* EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "ati.h"
|
|
#include "atichip.h"
|
|
#include "atii2c.h"
|
|
#include "atimach64i2c.h"
|
|
#include "atimach64io.h"
|
|
#include "atituner.h"
|
|
|
|
/* MPP_CONFIG register values */
|
|
#define MPP_INIT pATI->NewHW.mpp_config
|
|
|
|
#define MPP_WRITE (MPP_INIT )
|
|
#define MPP_WRITEINC (MPP_INIT | (MPP_AUTO_INC_EN ))
|
|
#define MPP_READ (MPP_INIT | ( MPP_BUFFER_MODE_PREFETCH))
|
|
#define MPP_READINC (MPP_INIT | (MPP_AUTO_INC_EN | MPP_BUFFER_MODE_PREFETCH))
|
|
|
|
/*
|
|
* ATIMach64MPPWaitForIdle --
|
|
*
|
|
* Support function to wait for the Multimedia Peripheral Port to become idle.
|
|
* Currently, this function's return value indicates whether or not the port
|
|
* became idle within 512 polling iterations. For now, this value is ignored
|
|
* by the rest of the code, but might be used in the future.
|
|
*/
|
|
static Bool
|
|
ATIMach64MPPWaitForIdle
|
|
(
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
CARD32 Count = 0x0200;
|
|
|
|
while (in8(MPP_CONFIG + 3) & GetByte(MPP_BUSY, 3))
|
|
{
|
|
if (!--Count)
|
|
return FALSE;
|
|
usleep(1); /* XXX Excessive? */
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ATIMach64MPPSetAddress --
|
|
*
|
|
* Sets a 16-bit ImpacTV address on the Multimedia Peripheral Port.
|
|
*/
|
|
static void
|
|
ATIMach64MPPSetAddress
|
|
(
|
|
ATIPtr pATI,
|
|
CARD16 Address
|
|
)
|
|
{
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_CONFIG, MPP_WRITEINC);
|
|
outr(MPP_ADDR, 0x00000008U);
|
|
out8(MPP_DATA, (CARD8)Address);
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
out8(MPP_DATA, (CARD8)(Address >> 8));
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_CONFIG, MPP_WRITE);
|
|
outr(MPP_ADDR, 0x00000018U);
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64ImpacTVProbe --
|
|
*
|
|
* This probes for an ImpacTV chip and returns its chip ID, or 0.
|
|
*/
|
|
static int
|
|
ATIMach64ImpacTVProbe
|
|
(
|
|
int iScreen,
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
CARD8 ChipID = 0;
|
|
|
|
/* Assume ATIModePreInit() has already been called */
|
|
outr(MPP_STROBE_SEQ, pATI->NewHW.mpp_strobe_seq);
|
|
outr(TVO_CNTL, pATI->NewHW.tvo_cntl);
|
|
|
|
outr(MPP_CONFIG, MPP_READ);
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_ADDR, 0x0000000AU);
|
|
if (!(ChipID = in8(MPP_DATA)))
|
|
{
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_ADDR, 0x00000023U);
|
|
if ((ChipID = in8(MPP_DATA)) != 0x54U)
|
|
{
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_ADDR, 0x0000000BU);
|
|
ChipID = in8(MPP_DATA);
|
|
}
|
|
}
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
outr(MPP_CONFIG, MPP_WRITE);
|
|
|
|
if (ChipID)
|
|
xf86DrvMsg(iScreen, X_PROBED, "ImpacTV chip ID 0x%02X detected.\n",
|
|
ChipID);
|
|
|
|
return (int)(CARD16)ChipID;
|
|
}
|
|
|
|
/*
|
|
* ATIMach64ImpacTVSetBits --
|
|
*
|
|
* Controls I2C SDA and SCL lines through ImpacTV.
|
|
*/
|
|
static void
|
|
ATIMach64ImpacTVSetBits
|
|
(
|
|
ATII2CPtr pATII2C,
|
|
ATIPtr pATI,
|
|
CARD32 Bits
|
|
)
|
|
{
|
|
pATII2C->I2CCur = Bits;
|
|
|
|
ATIMach64MPPSetAddress(pATI, IT_I2C_CNTL);
|
|
|
|
outr(MPP_CONFIG, MPP_WRITE);
|
|
|
|
out8(MPP_DATA, (CARD8)Bits);
|
|
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64ImpacTVGetBits --
|
|
*
|
|
* Returns the status of an ImpacTV's I2C control lines.
|
|
*/
|
|
static CARD32
|
|
ATIMach64ImpacTVGetBits
|
|
(
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
ATIMach64MPPSetAddress(pATI, IT_I2C_CNTL);
|
|
|
|
outr(MPP_CONFIG, MPP_READ);
|
|
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
|
|
return in8(MPP_DATA);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64I2C_CNTLSetBits --
|
|
*
|
|
* Controls SDA and SCL lines through a 3D Rage Pro's hardware assisted I2C.
|
|
*/
|
|
static void
|
|
ATIMach64I2C_CNTLSetBits
|
|
(
|
|
ATII2CPtr pATII2C,
|
|
ATIPtr pATI,
|
|
CARD32 Bits
|
|
)
|
|
{
|
|
pATII2C->I2CCur = Bits;
|
|
|
|
out8(I2C_CNTL_0 + 1, (CARD8)Bits);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64I2C_CNTLGetBits --
|
|
*
|
|
* Returns the status of a 3D Rage Pro's hardware assisted I2C control lines.
|
|
*/
|
|
static CARD32
|
|
ATIMach64I2C_CNTLGetBits
|
|
(
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
return in8(I2C_CNTL_0 + 1);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64GP_IOSetBits --
|
|
*
|
|
* Controls SDA and SCL control lines through a Mach64's GP_IO register.
|
|
*/
|
|
static void
|
|
ATIMach64GP_IOSetBits
|
|
(
|
|
ATII2CPtr pATII2C,
|
|
ATIPtr pATI,
|
|
CARD32 Bits
|
|
)
|
|
{
|
|
pATII2C->I2CCur = Bits;
|
|
|
|
outr(GP_IO, Bits);
|
|
}
|
|
|
|
/*
|
|
* ATIMach64GP_IOGetBits --
|
|
*
|
|
* Returns the status of I2C control lines through a Mach64's GP_IO register.
|
|
*/
|
|
static CARD32
|
|
ATIMach64GP_IOGetBits
|
|
(
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
return inr(GP_IO);
|
|
}
|
|
|
|
#define GPIO1_MASK \
|
|
(DAC_GIO_STATE_1 | DAC_GIO_DIR_1)
|
|
#define GPIO2_MASK \
|
|
(GEN_GIO2_DATA_OUT | GEN_GIO2_DATA_IN | GEN_GIO2_WRITE)
|
|
|
|
/*
|
|
* ATIMach64DAC_GENSetBits --
|
|
*
|
|
* Controls SDA and SCL control lines through a Mach64's GEN_TEST_CNTL and
|
|
* DAC_CNTL registers.
|
|
*/
|
|
static void
|
|
ATIMach64DAC_GENSetBits
|
|
(
|
|
ATII2CPtr pATII2C,
|
|
ATIPtr pATI,
|
|
CARD32 Bits
|
|
)
|
|
{
|
|
CARD32 tmp;
|
|
|
|
pATII2C->I2CCur = Bits;
|
|
|
|
tmp = inr(DAC_CNTL) & ~GPIO1_MASK;
|
|
outr(DAC_CNTL, tmp | (Bits & GPIO1_MASK));
|
|
tmp = inr(GEN_TEST_CNTL) & ~GPIO2_MASK;
|
|
outr(GEN_TEST_CNTL, tmp | (Bits & GPIO2_MASK));
|
|
}
|
|
|
|
/*
|
|
* ATIMach64DAC_GENGetBits --
|
|
*
|
|
* Returns the status of I2C control lines through a Mach64's GEN_TEST_CNTL and
|
|
* DAC_CNTL registers.
|
|
*/
|
|
static CARD32
|
|
ATIMach64DAC_GENGetBits
|
|
(
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
return (inr(DAC_CNTL) & GPIO1_MASK) | (inr(GEN_TEST_CNTL) & GPIO2_MASK);
|
|
}
|
|
|
|
/*
|
|
* ATITVAddOnProbe --
|
|
*
|
|
* Probe for an ATI-TV add-on card at specific addresses on an I2C bus.
|
|
*/
|
|
static Bool
|
|
ATITVAddOnProbe
|
|
(
|
|
ScrnInfoPtr pScreenInfo,
|
|
ATIPtr pATI,
|
|
I2CBusPtr pI2CBus
|
|
)
|
|
{
|
|
I2CDevPtr pI2CDev = xnfcalloc(1, SizeOf(I2CDevRec));
|
|
int Index;
|
|
I2CByte tmp;
|
|
|
|
static const CARD8 ATITVAddOnAddresses[] = {0x70, 0x40, 0x78, 0x72, 0x42};
|
|
|
|
pI2CDev->DevName = "ATI-TV Add-on";
|
|
pI2CDev->pI2CBus = pI2CBus;
|
|
pI2CDev->StartTimeout = pI2CBus->StartTimeout;
|
|
pI2CDev->BitTimeout = pI2CBus->BitTimeout;
|
|
pI2CDev->AcknTimeout = pI2CBus->AcknTimeout;
|
|
pI2CDev->ByteTimeout = pI2CBus->ByteTimeout;
|
|
|
|
for (Index = 0; Index < NumberOf(ATITVAddOnAddresses); Index++)
|
|
{
|
|
pI2CDev->SlaveAddr = ATITVAddOnAddresses[Index];
|
|
|
|
if (xf86I2CFindDev(pI2CBus, pI2CDev->SlaveAddr))
|
|
continue;
|
|
|
|
tmp = 0xFFU;
|
|
|
|
if (!(*pI2CBus->I2CWriteRead)(pI2CDev, &tmp, 1, NULL, 0) ||
|
|
!(*pI2CBus->I2CWriteRead)(pI2CDev, NULL, 0, &tmp, 1) ||
|
|
(tmp == 0xFFU) || ((tmp = tmp & 0x1FU) == /*ATI_TUNER_NONE*/0))
|
|
continue;
|
|
|
|
if (!xf86I2CDevInit(pI2CDev))
|
|
{
|
|
xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
|
|
"Failed to register I2C device for ATI-TV add-on.\n");
|
|
break;
|
|
}
|
|
|
|
if (pATI->Tuner != tmp)
|
|
{
|
|
if (pATI->Tuner != ATI_TUNER_NONE)
|
|
xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
|
|
"Tuner type mismatch: BIOS 0x%x, ATI-TV 0x%x.\n",
|
|
pATI->Tuner, tmp);
|
|
|
|
pATI->Tuner = tmp;
|
|
}
|
|
|
|
xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
|
|
"%s tuner detected on ATI-TV add-on adapter at I2C bus address"
|
|
" 0x%2x.\n", ATITuners[pATI->Tuner].name, pI2CDev->SlaveAddr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
xfree(pI2CDev);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* ATIMach64I2CPreInit --
|
|
*
|
|
* This function potentially allocates an I2CBusRec and initialises it with
|
|
* ATI-specific and Mach64-specific information.
|
|
*/
|
|
void
|
|
ATIMach64I2CPreInit
|
|
(
|
|
ScrnInfoPtr pScreenInfo,
|
|
ATIPtr pATI
|
|
)
|
|
{
|
|
I2CBusPtr pI2CBus;
|
|
ATII2CPtr pATII2C;
|
|
|
|
if ((pATI->Chip < ATI_CHIP_264CT) || (pATI->Chip >= ATI_CHIP_Mach64))
|
|
return;
|
|
|
|
/* Create an I2CBusRec and generically prime it */
|
|
if (!(pI2CBus = ATICreateI2CBusRec(pScreenInfo->scrnIndex, pATI, "Mach64")))
|
|
return;
|
|
|
|
pATII2C = pI2CBus->DriverPrivate.ptr;
|
|
|
|
switch (pATI->Chip)
|
|
{
|
|
case ATI_CHIP_264GTPRO:
|
|
case ATI_CHIP_264LTPRO:
|
|
case ATI_CHIP_264XL:
|
|
case ATI_CHIP_MOBILITY:
|
|
/*
|
|
* These have I2C-specific registers. Assume older I2C access
|
|
* mechanisms are inoperative.
|
|
*/
|
|
pATII2C->I2CSetBits = ATIMach64I2C_CNTLSetBits;
|
|
pATII2C->I2CGetBits = ATIMach64I2C_CNTLGetBits;
|
|
pATII2C->SCLDir = pATII2C->SDADir = 0;
|
|
pATII2C->SCLGet = pATII2C->SCLSet = GetByte(I2C_CNTL_SCL, 1);
|
|
pATII2C->SDAGet = pATII2C->SDASet = GetByte(I2C_CNTL_SDA, 1);
|
|
|
|
out8(I2C_CNTL_1 + 2, GetByte(I2C_SEL, 2));
|
|
out8(I2C_CNTL_0 + 0,
|
|
GetByte(I2C_CNTL_STAT | I2C_CNTL_HPTR_RST, 0));
|
|
break;
|
|
|
|
case ATI_CHIP_264VTB:
|
|
case ATI_CHIP_264GTB:
|
|
case ATI_CHIP_264VT3:
|
|
case ATI_CHIP_264GTDVD:
|
|
case ATI_CHIP_264LT:
|
|
case ATI_CHIP_264VT4:
|
|
case ATI_CHIP_264GT2C:
|
|
/* If an ImpacTV chip is found, use it to provide I2C access */
|
|
if (ATIMach64ImpacTVProbe(pScreenInfo->scrnIndex, pATI))
|
|
{
|
|
pATII2C->I2CSetBits = ATIMach64ImpacTVSetBits;
|
|
pATII2C->I2CGetBits = ATIMach64ImpacTVGetBits;
|
|
pATII2C->SCLDir = IT_SCL_DIR;
|
|
pATII2C->SCLGet = IT_SCL_GET;
|
|
pATII2C->SCLSet = IT_SCL_SET;
|
|
pATII2C->SDADir = IT_SDA_DIR;
|
|
pATII2C->SDAGet = IT_SDA_GET;
|
|
pATII2C->SDASet = IT_SDA_SET;
|
|
|
|
ATIMach64MPPSetAddress(pATI, IT_I2C_CNTL);
|
|
outr(MPP_CONFIG, MPP_WRITEINC);
|
|
out8(MPP_DATA, 0x00U);
|
|
out8(MPP_DATA, 0x55U);
|
|
out8(MPP_DATA, 0x00U);
|
|
out8(MPP_DATA, 0x00U);
|
|
ATIMach64MPPWaitForIdle(pATI);
|
|
break;
|
|
}
|
|
/* Otherwise, fall through to the older case */
|
|
|
|
case ATI_CHIP_264VT:
|
|
case ATI_CHIP_264GT:
|
|
/* First try GIO pins 11 (clock) and 4 (data) */
|
|
pATII2C->I2CSetBits = ATIMach64GP_IOSetBits;
|
|
pATII2C->I2CGetBits = ATIMach64GP_IOGetBits;
|
|
pATII2C->SCLDir = GP_IO_DIR_B;
|
|
pATII2C->SCLGet = pATII2C->SCLSet = GP_IO_B;
|
|
pATII2C->SDADir = GP_IO_DIR_4;
|
|
pATII2C->SDAGet = pATII2C->SDASet = GP_IO_4;
|
|
|
|
if (ATITVAddOnProbe(pScreenInfo, pATI, pI2CBus))
|
|
break;
|
|
|
|
/* Next, try pins 10 (clock) and 12 (data) */
|
|
pATII2C->SCLDir = GP_IO_DIR_A;
|
|
pATII2C->SCLGet = pATII2C->SCLSet = GP_IO_A;
|
|
pATII2C->SDADir = GP_IO_DIR_C;
|
|
pATII2C->SDAGet = pATII2C->SDASet = GP_IO_C;
|
|
|
|
if (ATITVAddOnProbe(pScreenInfo, pATI, pI2CBus))
|
|
break;
|
|
/* Otherwise, fall back to ATI's first I2C implementation */
|
|
|
|
default:
|
|
/*
|
|
* First generation integrated controllers access GIO pin 1 (clock)
|
|
* though DAC_CNTL, and pin 2 (data) through GEN_TEST_CNTL.
|
|
*/
|
|
pATII2C->I2CSetBits = ATIMach64DAC_GENSetBits;
|
|
pATII2C->I2CGetBits = ATIMach64DAC_GENGetBits;
|
|
pATII2C->SCLDir = DAC_GIO_DIR_1;
|
|
pATII2C->SCLGet = pATII2C->SCLSet = DAC_GIO_STATE_1;
|
|
pATII2C->SDADir = GEN_GIO2_WRITE;
|
|
pATII2C->SDAGet = GEN_GIO2_DATA_IN;
|
|
pATII2C->SDASet = GEN_GIO2_DATA_OUT;
|
|
|
|
(void)ATITVAddOnProbe(pScreenInfo, pATI, pI2CBus);
|
|
break;
|
|
}
|
|
}
|