868 lines
26 KiB
C
868 lines
26 KiB
C
/*
|
|
* Copyright 2007 Egbert Eich <eich@novell.com>
|
|
* Copyright 2007 Luc Verhaegen <lverhaegen@novell.com>
|
|
* Copyright 2007 Matthias Hopf <mhopf@novell.com>
|
|
* Copyright 2007 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "xf86.h"
|
|
#include "xf86_OSproc.h"
|
|
#include "xf86_ansic.h"
|
|
#include "xf86i2c.h"
|
|
|
|
#include "rhd.h"
|
|
#include "rhd_i2c.h"
|
|
#include "rhd_regs.h"
|
|
|
|
#ifdef ATOM_BIOS
|
|
#include "rhd_atombios.h"
|
|
#endif
|
|
|
|
typedef struct _rhdI2CRec
|
|
{
|
|
CARD16 prescale;
|
|
CARD8 line;
|
|
int scrnIndex;
|
|
} rhdI2CRec;
|
|
|
|
enum _rhdR6xxI2CBits {
|
|
/* R6_DC_I2C_TRANSACTION0 */
|
|
R6_DC_I2C_RW0 = (0x1 << 0),
|
|
R6_DC_I2C_STOP_ON_NACK0 = (0x1 << 8),
|
|
R6_DC_I2C_ACK_ON_READ0 = (0x1 << 9),
|
|
R6_DC_I2C_START0 = (0x1 << 12),
|
|
R6_DC_I2C_STOP0 = (0x1 << 13),
|
|
R6_DC_I2C_COUNT0 = (0xff << 16),
|
|
/* R6_DC_I2C_TRANSACTION1 */
|
|
R6_DC_I2C_RW1 = (0x1 << 0),
|
|
R6_DC_I2C_STOP_ON_NACK1 = (0x1 << 8),
|
|
R6_DC_I2C_ACK_ON_READ1 = (0x1 << 9),
|
|
R6_DC_I2C_START1 = (0x1 << 12),
|
|
R6_DC_I2C_STOP1 = (0x1 << 13),
|
|
R6_DC_I2C_COUNT1 = (0xff << 16),
|
|
/* R6_DC_I2C_DATA */
|
|
R6_DC_I2C_DATA_RW = (0x1 << 0),
|
|
R6_DC_I2C_DATA_BIT = (0xff << 8),
|
|
R6_DC_I2C_INDEX = (0xff << 16),
|
|
R6_DC_I2C_INDEX_WRITE = (0x1 << 31),
|
|
/* R6_DC_I2C_CONTROL */
|
|
R6_DC_I2C_GO = (0x1 << 0),
|
|
R6_DC_I2C_SOFT_RESET = (0x1 << 1),
|
|
R6_DC_I2C_SEND_RESET = (0x1 << 2),
|
|
R6_DC_I2C_SW_STATUS_RESET = (0x1 << 3),
|
|
R6_DC_I2C_SDVO_EN = (0x1 << 4),
|
|
R6_DC_I2C_SDVO_ADDR_SEL = (0x1 << 6),
|
|
R6_DC_I2C_DDC_SELECT = (0x7 << 8),
|
|
R6_DC_I2C_TRANSACTION_COUNT = (0x3 << 20),
|
|
R6_DC_I2C_SW_DONE_INT = (0x1 << 0),
|
|
R6_DC_I2C_SW_DONE_ACK = (0x1 << 1),
|
|
R6_DC_I2C_SW_DONE_MASK = (0x1 << 2),
|
|
R6_DC_I2C_DDC1_HW_DONE_INT = (0x1 << 4),
|
|
R6_DC_I2C_DDC1_HW_DONE_ACK = (0x1 << 5),
|
|
R6_DC_I2C_DDC1_HW_DONE_MASK = (0x1 << 6),
|
|
R6_DC_I2C_DDC2_HW_DONE_INT = (0x1 << 8),
|
|
R6_DC_I2C_DDC2_HW_DONE_ACK = (0x1 << 9),
|
|
R6_DC_I2C_DDC2_HW_DONE_MASK = (0x1 << 10),
|
|
R6_DC_I2C_DDC3_HW_DONE_INT = (0x1 << 12),
|
|
R6_DC_I2C_DDC3_HW_DONE_ACK = (0x1 << 13),
|
|
R6_DC_I2C_DDC3_HW_DONE_MASK = (0x1 << 14),
|
|
R6_DC_I2C_DDC4_HW_DONE_INT = (0x1 << 16),
|
|
R6_DC_I2C_DDC4_HW_DONE_ACK = (0x1 << 17),
|
|
R6_DC_I2C_DDC4_HW_DONE_MASK = (0x1 << 18),
|
|
/* R6_DC_I2C_SW_STATUS */
|
|
R6_DC_I2C_SW_STATUS_BIT = (0x3 << 0),
|
|
R6_DC_I2C_SW_DONE = (0x1 << 2),
|
|
R6_DC_I2C_SW_ABORTED = (0x1 << 4),
|
|
R6_DC_I2C_SW_TIMEOUT = (0x1 << 5),
|
|
R6_DC_I2C_SW_INTERRUPTED = (0x1 << 6),
|
|
R6_DC_I2C_SW_BUFFER_OVERFLOW = (0x1 << 7),
|
|
R6_DC_I2C_SW_STOPPED_ON_NACK = (0x1 << 8),
|
|
R6_DC_I2C_SW_SDVO_NACK = (0x1 << 10),
|
|
R6_DC_I2C_SW_NACK0 = (0x1 << 12),
|
|
R6_DC_I2C_SW_NACK1 = (0x1 << 13),
|
|
R6_DC_I2C_SW_NACK2 = (0x1 << 14),
|
|
R6_DC_I2C_SW_NACK3 = (0x1 << 15),
|
|
R6_DC_I2C_SW_REQ = (0x1 << 18)
|
|
};
|
|
|
|
enum _rhdR5xxI2CBits {
|
|
/* R5_DC_I2C_STATUS1 */
|
|
R5_DC_I2C_DONE = (0x1 << 0),
|
|
R5_DC_I2C_NACK = (0x1 << 1),
|
|
R5_DC_I2C_HALT = (0x1 << 2),
|
|
R5_DC_I2C_GO = (0x1 << 3),
|
|
/* R5_DC_I2C_RESET */
|
|
R5_DC_I2C_SOFT_RESET = (0x1 << 0),
|
|
R5_DC_I2C_ABORT = (0x1 << 8),
|
|
/* R5_DC_I2C_CONTROL1 */
|
|
R5_DC_I2C_START = (0x1 << 0),
|
|
R5_DC_I2C_STOP = (0x1 << 1),
|
|
R5_DC_I2C_RECEIVE = (0x1 << 2),
|
|
R5_DC_I2C_EN = (0x1 << 8),
|
|
R5_DC_I2C_PIN_SELECT = (0x3 << 16),
|
|
/* R5_DC_I2C_CONTROL2 */
|
|
R5_DC_I2C_ADDR_COUNT = (0x7 << 0),
|
|
R5_DC_I2C_DATA_COUNT = (0xf << 8),
|
|
R5_DC_I2C_PRESCALE_LOWER = (0xff << 16),
|
|
R5_DC_I2C_PRESCALE_UPPER = (0xff << 24),
|
|
/* R5_DC_I2C_CONTROL3 */
|
|
R5_DC_I2C_DATA_DRIVE_EN = (0x1 << 0),
|
|
R5_DC_I2C_DATA_DRIVE_SEL = (0x1 << 1),
|
|
R5_DC_I2C_CLK_DRIVE_EN = (0x1 << 7),
|
|
R5_DC_I2C_RD_INTRA_BYTE_DELAY = (0xff << 8),
|
|
R5_DC_I2C_WR_INTRA_BYTE_DELAY = (0xff << 16),
|
|
R5_DC_I2C_TIME_LIMIT = (0xff << 24),
|
|
/* R5_DC_I2C_DATA */
|
|
R5_DC_I2C_DATA_BIT = (0xff << 0),
|
|
/* R5_DC_I2C_INTERRUPT_CONTROL */
|
|
R5_DC_I2C_INTERRUPT_STATUS = (0x1 << 0),
|
|
R5_DC_I2C_INTERRUPT_AK = (0x1 << 8),
|
|
R5_DC_I2C_INTERRUPT_ENABLE = (0x1 << 16),
|
|
/* R5_DC_I2C_ARBITRATION */
|
|
R5_DC_I2C_SW_WANTS_TO_USE_I2C = (0x1 << 0),
|
|
R5_DC_I2C_SW_CAN_USE_I2C = (0x1 << 1),
|
|
R5_DC_I2C_SW_DONE_USING_I2C = (0x1 << 8),
|
|
R5_DC_I2C_HW_NEEDS_I2C = (0x1 << 9),
|
|
R5_DC_I2C_ABORT_HDCP_I2C = (0x1 << 16),
|
|
R5_DC_I2C_HW_USING_I2C = (0x1 << 17)
|
|
};
|
|
|
|
enum _rhdRS69I2CBits {
|
|
/* RS69_DC_I2C_TRANSACTION0 */
|
|
RS69_DC_I2C_RW0 = (0x1 << 0),
|
|
RS69_DC_I2C_STOP_ON_NACK0 = (0x1 << 8),
|
|
RS69_DC_I2C_START0 = (0x1 << 12),
|
|
RS69_DC_I2C_STOP0 = (0x1 << 13),
|
|
/* RS69_DC_I2C_TRANSACTION1 */
|
|
RS69_DC_I2C_RW1 = (0x1 << 0),
|
|
RS69_DC_I2C_START1 = (0x1 << 12),
|
|
RS69_DC_I2C_STOP1 = (0x1 << 13),
|
|
/* RS69_DC_I2C_DATA */
|
|
RS69_DC_I2C_DATA_RW = (0x1 << 0),
|
|
RS69_DC_I2C_INDEX_WRITE = (0x1 << 31),
|
|
/* RS69_DC_I2C_CONTROL */
|
|
RS69_DC_I2C_GO = (0x1 << 0),
|
|
RS69_DC_I2C_TRANSACTION_COUNT = (0x3 << 20),
|
|
RS69_DC_I2C_SW_DONE_ACK = (0x1 << 1),
|
|
/* RS69_DC_I2C_SW_STATUS */
|
|
RS69_DC_I2C_SW_DONE = (0x1 << 2),
|
|
RS69_DC_I2C_SW_STOPPED_ON_NACK = (0x1 << 8),
|
|
RS69_DC_I2C_SW_NACK0 = (0x1 << 12),
|
|
RS69_DC_I2C_SW_NACK1 = (0x1 << 13)
|
|
};
|
|
|
|
/* R5xx */
|
|
static Bool
|
|
rhd5xxI2CStatus(I2CBusPtr I2CPtr)
|
|
{
|
|
int count = 5000;
|
|
CARD32 res;
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
while (count-- != 0) {
|
|
usleep (10);
|
|
if (((RHDRegRead(I2CPtr, R5_DC_I2C_STATUS1)) & R5_DC_I2C_GO) != 0)
|
|
continue;
|
|
res = RHDRegRead(I2CPtr, R5_DC_I2C_STATUS1);
|
|
RHDDebugVerb(I2CPtr->scrnIndex,1,"SW_STATUS: 0x%x %i\n",
|
|
(unsigned int)res,count);
|
|
if (res & R5_DC_I2C_DONE)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_RESET, R5_DC_I2C_ABORT, 0xff00);
|
|
return FALSE;
|
|
}
|
|
|
|
Bool
|
|
rhd5xxWriteReadChunk(I2CDevPtr i2cDevPtr, I2CByte *WriteBuffer,
|
|
int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
{
|
|
I2CSlaveAddr slave = i2cDevPtr->SlaveAddr;
|
|
rhdI2CPtr I2C = (rhdI2CPtr)(i2cDevPtr->pI2CBus->DriverPrivate.ptr);
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
CARD8 line = I2C->line;
|
|
int prescale = I2C->prescale;
|
|
CARD32 save_I2C_CONTROL1, save_494;
|
|
CARD32 tmp32;
|
|
Bool ret = TRUE;
|
|
|
|
RHDFUNC(i2cDevPtr->pI2CBus);
|
|
|
|
RHDRegMask(I2CPtr, 0x28, 0x200, 0x200);
|
|
save_I2C_CONTROL1 = RHDRegRead(I2CPtr, R5_DC_I2C_CONTROL1);
|
|
save_494 = RHDRegRead(I2CPtr, 0x494);
|
|
RHDRegMask(I2CPtr, 0x494, 1, 1);
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_ARBITRATION,
|
|
R5_DC_I2C_SW_WANTS_TO_USE_I2C,
|
|
R5_DC_I2C_SW_WANTS_TO_USE_I2C);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1, R5_DC_I2C_DONE
|
|
| R5_DC_I2C_NACK
|
|
| R5_DC_I2C_HALT, 0xff);
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_RESET, R5_DC_I2C_SOFT_RESET, 0xffff);
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_RESET, 0);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_CONTROL1,
|
|
(line & 0x0f) << 16 | R5_DC_I2C_EN,
|
|
R5_DC_I2C_PIN_SELECT | R5_DC_I2C_EN);
|
|
|
|
if (nWrite || !nRead) { /* special case for bus probing */
|
|
/*
|
|
* chip can't just write the slave address without data.
|
|
* Add a dummy byte.
|
|
*/
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_CONTROL2,
|
|
prescale << 16 |
|
|
(nWrite ? nWrite : 1) << 8 | 0x01); /* addr_cnt: 1 */
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_CONTROL3,
|
|
0x30 << 24, 0xff << 24); /* time limit 30 */
|
|
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_DATA, slave);
|
|
|
|
/* Add dummy byte */
|
|
if (!nWrite)
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_DATA, 0);
|
|
else
|
|
while (nWrite--)
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_DATA, *WriteBuffer++);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_CONTROL1,
|
|
R5_DC_I2C_START | R5_DC_I2C_STOP, 0xff);
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1, R5_DC_I2C_GO, 0xff);
|
|
|
|
if ((ret = rhd5xxI2CStatus(I2CPtr)))
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1,R5_DC_I2C_DONE, 0xff);
|
|
else
|
|
ret = FALSE;
|
|
}
|
|
|
|
if (ret && nRead) {
|
|
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_DATA, slave | 1); /*slave*/
|
|
RHDRegWrite(I2CPtr, R5_DC_I2C_CONTROL2,
|
|
prescale << 16 | nRead << 8 | 0x01); /* addr_cnt: 1 */
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_CONTROL1,
|
|
R5_DC_I2C_START | R5_DC_I2C_STOP | R5_DC_I2C_RECEIVE, 0xff);
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1, R5_DC_I2C_GO, 0xff);
|
|
if ((ret = rhd5xxI2CStatus(I2CPtr))) {
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1, R5_DC_I2C_DONE, 0xff);
|
|
while (nRead--) {
|
|
*(ReadBuffer++) = (CARD8)RHDRegRead(I2CPtr, R5_DC_I2C_DATA);
|
|
}
|
|
} else
|
|
ret = FALSE;
|
|
}
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_STATUS1,
|
|
R5_DC_I2C_DONE | R5_DC_I2C_NACK | R5_DC_I2C_HALT, 0xff);
|
|
RHDRegMask(I2CPtr, R5_DC_I2C_RESET, R5_DC_I2C_SOFT_RESET, 0xff);
|
|
RHDRegWrite(I2CPtr,R5_DC_I2C_RESET, 0);
|
|
|
|
RHDRegMask(I2CPtr,R5_DC_I2C_ARBITRATION,
|
|
R5_DC_I2C_SW_DONE_USING_I2C, 0xff00);
|
|
|
|
RHDRegWrite(I2CPtr,R5_DC_I2C_CONTROL1, save_I2C_CONTROL1);
|
|
RHDRegWrite(I2CPtr,0x494, save_494);
|
|
tmp32 = RHDRegRead(I2CPtr,0x28);
|
|
RHDRegWrite(I2CPtr,0x28, tmp32 & 0xfffffdff);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static Bool
|
|
rhd5xxWriteRead(I2CDevPtr i2cDevPtr, I2CByte *WriteBuffer, int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
{
|
|
/*
|
|
* Since the transaction buffer can only hold
|
|
* 15 bytes (+ the slave address) we bail out
|
|
* on every transaction that is bigger unless
|
|
* it's a read transaction following a write
|
|
* transaction sending just one byte.
|
|
* In this case we assume, that this byte is
|
|
* an offset address. Thus we will restart
|
|
* the transaction after 15 bytes sending
|
|
* a new offset.
|
|
*/
|
|
RHDFUNC(i2cDevPtr->pI2CBus);
|
|
|
|
if (nWrite > 15 || (nRead > 15 && nWrite != 1)) {
|
|
xf86DrvMsg(i2cDevPtr->pI2CBus->scrnIndex,X_ERROR,
|
|
"%s: Currently only I2C transfers with "
|
|
"maximally 15bytes are supported\n",
|
|
__func__);
|
|
return FALSE;
|
|
}
|
|
if (nRead > 15) {
|
|
I2CByte offset = *WriteBuffer;
|
|
while (nRead) {
|
|
int n = nRead > 15 ? 15 : nRead;
|
|
if (!rhd5xxWriteReadChunk(i2cDevPtr, &offset, 1, ReadBuffer, n))
|
|
return FALSE;
|
|
ReadBuffer += n;
|
|
nRead -= n;
|
|
offset += n;
|
|
}
|
|
return TRUE;
|
|
} else
|
|
return rhd5xxWriteReadChunk(i2cDevPtr, WriteBuffer, nWrite,
|
|
ReadBuffer, nRead);
|
|
}
|
|
|
|
/* RS690 */
|
|
static Bool
|
|
rhdRS69I2CStatus(I2CBusPtr I2CPtr)
|
|
{
|
|
int count = 5000;
|
|
volatile CARD32 val;
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
while (--count) {
|
|
|
|
usleep(10);
|
|
val = RHDRegRead(I2CPtr, RS69_DC_I2C_SW_STATUS);
|
|
RHDDebugVerb(I2CPtr->scrnIndex,1,"SW_STATUS: 0x%x %i\n",(unsigned int)val,count);
|
|
if (val & RS69_DC_I2C_SW_DONE)
|
|
break;
|
|
}
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_INTERRUPT_CONTROL, RS69_DC_I2C_SW_DONE_ACK,
|
|
RS69_DC_I2C_SW_DONE_ACK);
|
|
if (!count || (val & (RS69_DC_I2C_SW_STOPPED_ON_NACK | RS69_DC_I2C_SW_NACK0 | RS69_DC_I2C_SW_NACK1 | 0x3)))
|
|
return FALSE; /* 2 */
|
|
return TRUE; /* 1 */
|
|
}
|
|
|
|
static Bool
|
|
rhdRS69I2CSetupStatus(I2CBusPtr I2CPtr, int line, int prescale)
|
|
{
|
|
AtomBiosArgRec atomBiosArg;
|
|
CARD32 ddc;
|
|
RHDPtr rhdPtr = RHDPTR(xf86Screens[I2CPtr->scrnIndex]);
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
atomBiosArg.val = line & 0xf;
|
|
if (ATOM_SUCCESS != RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS,
|
|
ATOM_GPIO_I2C_CLK_MASK,
|
|
&atomBiosArg))
|
|
return FALSE;
|
|
|
|
RHDRegMask(I2CPtr, 0x28, 0x200, 0x200);
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_UNKNOWN_1, prescale << 16 | 0x2, 0xffff00ff);
|
|
/* add SDVO handling later */
|
|
switch (atomBiosArg.val) {
|
|
case 0x1f90:
|
|
ddc = 0; /* ddc1 */
|
|
break;
|
|
case 0x1f94: /* ddc2 */
|
|
ddc = 1;
|
|
break;
|
|
default:
|
|
ddc = 2; /* ddc3 */
|
|
break;
|
|
}
|
|
RHDDebug(I2CPtr->scrnIndex,"%s: DDC Line: %i val: %i port: 0x%x\n",
|
|
__func__,line & 0xf, ddc, atomBiosArg.val);
|
|
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, ddc << 8, 0xff << 8);
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DDC_SETUP_Q, 0x30000000);
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, (line & 0x3) << 16, 0xff << 16);
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_INTERRUPT_CONTROL, 0x2, 0x2);
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_UNKNOWN_2, 0x2, 0xff);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
rhdRS69WriteRead(I2CDevPtr i2cDevPtr, I2CByte *WriteBuffer,
|
|
int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
{
|
|
Bool ret = FALSE;
|
|
CARD32 data = 0;
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
I2CSlaveAddr slave = i2cDevPtr->SlaveAddr;
|
|
rhdI2CPtr I2C = (rhdI2CPtr)I2CPtr->DriverPrivate.ptr;
|
|
CARD8 line = I2C->line;
|
|
int prescale = I2C->prescale;
|
|
int idx = 1;
|
|
|
|
enum {
|
|
TRANS_WRITE_READ,
|
|
TRANS_WRITE,
|
|
TRANS_READ
|
|
} trans;
|
|
|
|
RHDFUNC(i2cDevPtr->pI2CBus);
|
|
|
|
if (nWrite > 0 && nRead > 0) {
|
|
trans = TRANS_WRITE_READ;
|
|
} else if (nWrite > 0) {
|
|
trans = TRANS_WRITE;
|
|
} else if (nRead > 0) {
|
|
trans = TRANS_READ;
|
|
} else {
|
|
/* for bus probing */
|
|
trans = TRANS_WRITE;
|
|
}
|
|
if (slave & 0xff00) {
|
|
xf86DrvMsg(I2CPtr->scrnIndex,X_ERROR,
|
|
"%s: 10 bit I2C slave addresses not supported\n",__func__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!rhdRS69I2CSetupStatus(I2CPtr, line, prescale))
|
|
return FALSE;
|
|
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, (trans == TRANS_WRITE_READ)
|
|
? (1 << 20) : 0, RS69_DC_I2C_TRANSACTION_COUNT); /* 2 or 1 Transaction */
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_TRANSACTION0,
|
|
RS69_DC_I2C_STOP_ON_NACK0
|
|
| (trans == TRANS_READ ? RS69_DC_I2C_RW0 : 0)
|
|
| RS69_DC_I2C_START0
|
|
| (trans == TRANS_WRITE_READ ? 0 : RS69_DC_I2C_STOP0 )
|
|
| ((trans == TRANS_READ ? nRead : nWrite) << 16),
|
|
0xffffff);
|
|
if (trans == TRANS_WRITE_READ)
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_TRANSACTION1,
|
|
nRead << 16
|
|
| RS69_DC_I2C_RW1
|
|
| RS69_DC_I2C_START1
|
|
| RS69_DC_I2C_STOP1,
|
|
0xffffff); /* <bytes> read */
|
|
|
|
data = RS69_DC_I2C_INDEX_WRITE
|
|
| (((slave & 0xfe) | (trans == TRANS_READ ? 1 : 0)) << 8 )
|
|
| (0 << 16);
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DATA, data);
|
|
if (trans != TRANS_READ) { /* we have bytes to write */
|
|
while (nWrite--) {
|
|
data = RS69_DC_I2C_INDEX_WRITE | ( *(WriteBuffer++) << 8 )
|
|
| (idx++ << 16);
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DATA, data);
|
|
}
|
|
}
|
|
if (trans == TRANS_WRITE_READ) { /* we have bytes to read after write */
|
|
data = RS69_DC_I2C_INDEX_WRITE | ((slave | 0x1) << 8) | (idx++ << 16);
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DATA, data);
|
|
}
|
|
/* Go! */
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, RS69_DC_I2C_GO, RS69_DC_I2C_GO);
|
|
if (rhdRS69I2CStatus(I2CPtr)) {
|
|
/* Hopefully this doesn't write data to index */
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DATA, RS69_DC_I2C_INDEX_WRITE
|
|
| RS69_DC_I2C_DATA_RW | /* idx++ */3 << 16);
|
|
while (nRead--) {
|
|
data = RHDRegRead(I2CPtr, RS69_DC_I2C_DATA);
|
|
*(ReadBuffer++) = (data >> 8) & 0xff;
|
|
}
|
|
ret = TRUE;
|
|
}
|
|
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, 0x2, 0xff);
|
|
usleep(10);
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_CONTROL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* R6xx */
|
|
static Bool
|
|
rhdR6xxI2CStatus(I2CBusPtr I2CPtr)
|
|
{
|
|
int count = 5000;
|
|
volatile CARD32 val;
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
while (--count) {
|
|
|
|
usleep(10);
|
|
val = RHDRegRead(I2CPtr, R6_DC_I2C_SW_STATUS);
|
|
RHDDebugVerb(I2CPtr->scrnIndex,1,"SW_STATUS: 0x%x %i\n",(unsigned int)val,count);
|
|
if (val & R6_DC_I2C_SW_DONE)
|
|
break;
|
|
}
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_INTERRUPT_CONTROL, R6_DC_I2C_SW_DONE_ACK,
|
|
R6_DC_I2C_SW_DONE_ACK);
|
|
if (!count || (val & (R6_DC_I2C_SW_STOPPED_ON_NACK | R6_DC_I2C_SW_NACK0 | R6_DC_I2C_SW_NACK1 | 0x3)))
|
|
return FALSE; /* 2 */
|
|
return TRUE; /* 1 */
|
|
}
|
|
|
|
static Bool
|
|
rhd6xxI2CSetupStatus(I2CBusPtr I2CPtr, int line, int prescale)
|
|
{
|
|
line &= 0xf;
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
switch (line) {
|
|
case 0:
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC1_MASK, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC1_A, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC1_EN, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_DDC1_SPEED, (prescale << 16) | 2,
|
|
0xffff00ff);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DDC1_SETUP, 0x30000000);
|
|
break;
|
|
case 1:
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC2_MASK, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC2_A, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC2_EN, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_DDC2_SPEED, (prescale << 16) | 2,
|
|
0xffff00ff);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DDC2_SETUP, 0x30000000);
|
|
break;
|
|
case 2:
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC3_MASK, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC3_A, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC3_EN, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_DDC3_SPEED, (prescale << 16) | 2,
|
|
0xffff00ff);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DDC3_SETUP, 0x30000000);
|
|
break;
|
|
case 3:
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC4_MASK, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC4_A, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC4_EN, 0x0, 0xffff);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_DDC4_SPEED, (prescale << 16) | 2,
|
|
0xffff00ff);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DDC4_SETUP, 0x30000000);
|
|
break;
|
|
default:
|
|
xf86DrvMsg(I2CPtr->scrnIndex,X_ERROR,
|
|
"%s: Trying to initialize non-existent I2C line: %i\n",
|
|
__func__,line);
|
|
return FALSE;
|
|
}
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_CONTROL, line << 8);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_INTERRUPT_CONTROL, 0x2, 0x2);
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_ARBITRATION, 0, 0xff);
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
rhd6xxWriteRead(I2CDevPtr i2cDevPtr, I2CByte *WriteBuffer, int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
{
|
|
Bool ret = FALSE;
|
|
CARD32 data = 0;
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
I2CSlaveAddr slave = i2cDevPtr->SlaveAddr;
|
|
rhdI2CPtr I2C = (rhdI2CPtr)I2CPtr->DriverPrivate.ptr;
|
|
CARD8 line = I2C->line;
|
|
int prescale = I2C->prescale;
|
|
int idx = 1;
|
|
|
|
enum {
|
|
TRANS_WRITE_READ,
|
|
TRANS_WRITE,
|
|
TRANS_READ
|
|
} trans;
|
|
|
|
RHDFUNC(i2cDevPtr->pI2CBus);
|
|
|
|
if (nWrite > 0 && nRead > 0) {
|
|
trans = TRANS_WRITE_READ;
|
|
} else if (nWrite > 0) {
|
|
trans = TRANS_WRITE;
|
|
} else if (nRead > 0) {
|
|
trans = TRANS_READ;
|
|
} else {
|
|
/* for bus probing */
|
|
trans = TRANS_WRITE;
|
|
}
|
|
if (slave & 0xff00) {
|
|
xf86DrvMsg(I2CPtr->scrnIndex,X_ERROR,
|
|
"%s: 10 bit I2C slave addresses not supported\n",__func__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!rhd6xxI2CSetupStatus(I2CPtr, line, prescale))
|
|
return FALSE;
|
|
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_CONTROL, (trans == TRANS_WRITE_READ)
|
|
? (1 << 20) : 0, R6_DC_I2C_TRANSACTION_COUNT); /* 2 or 1 Transaction */
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_TRANSACTION0,
|
|
R6_DC_I2C_STOP_ON_NACK0
|
|
| (trans == TRANS_READ ? R6_DC_I2C_RW0 : 0)
|
|
| R6_DC_I2C_START0
|
|
| (trans == TRANS_WRITE_READ ? 0 : R6_DC_I2C_STOP0 )
|
|
| ((trans == TRANS_READ ? nRead : nWrite) << 16),
|
|
0xffffff);
|
|
if (trans == TRANS_WRITE_READ)
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_TRANSACTION1,
|
|
nRead << 16
|
|
| R6_DC_I2C_RW1
|
|
| R6_DC_I2C_START1
|
|
| R6_DC_I2C_STOP1,
|
|
0xffffff); /* <bytes> read */
|
|
|
|
data = R6_DC_I2C_INDEX_WRITE
|
|
| (((slave & 0xfe) | (trans == TRANS_READ ? 1 : 0)) << 8 )
|
|
| (0 << 16);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DATA, data);
|
|
if (trans != TRANS_READ) { /* we have bytes to write */
|
|
while (nWrite--) {
|
|
data = R6_DC_I2C_INDEX_WRITE | ( *(WriteBuffer++) << 8 )
|
|
| (idx++ << 16);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DATA, data);
|
|
}
|
|
}
|
|
if (trans == TRANS_WRITE_READ) { /* we have bytes to read after write */
|
|
data = R6_DC_I2C_INDEX_WRITE | ((slave | 0x1) << 8) | (idx++ << 16);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DATA, data);
|
|
}
|
|
/* Go! */
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_CONTROL, R6_DC_I2C_GO, R6_DC_I2C_GO);
|
|
if (rhdR6xxI2CStatus(I2CPtr)) {
|
|
/* Hopefully this doesn't write data to index */
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_DATA, R6_DC_I2C_INDEX_WRITE
|
|
| R6_DC_I2C_DATA_RW | /* idx++ */3 << 16);
|
|
while (nRead--) {
|
|
data = RHDRegRead(I2CPtr, R6_DC_I2C_DATA);
|
|
*(ReadBuffer++) = (data >> 8) & 0xff;
|
|
}
|
|
ret = TRUE;
|
|
}
|
|
|
|
RHDRegMask(I2CPtr, R6_DC_I2C_CONTROL, 0x2, 0xff);
|
|
usleep(10);
|
|
RHDRegWrite(I2CPtr, R6_DC_I2C_CONTROL, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
rhdTearDownI2C(I2CBusPtr *I2C)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* xf86I2CGetScreenBuses() is
|
|
* broken in older server versions.
|
|
* So we cannot use it. How bad!
|
|
*/
|
|
for (i = 0; i < I2C_LINES; i++) {
|
|
char *name;
|
|
if (!I2C[i])
|
|
break;
|
|
name = I2C[i]->BusName;
|
|
xfree(I2C[i]->DriverPrivate.ptr);
|
|
xf86DestroyI2CBusRec(I2C[i], TRUE, TRUE);
|
|
xfree(name);
|
|
}
|
|
xfree(I2C);
|
|
}
|
|
|
|
#define TARGET_HW_I2C_CLOCK 25 /* kHz */
|
|
#define DEFAULT_ENGINE_CLOCK 700000 /* kHz (guessed) */
|
|
static CARD32
|
|
rhdGetI2CPrescale(RHDPtr rhdPtr)
|
|
{
|
|
#ifdef ATOM_BIOS
|
|
AtomBiosArgRec atomBiosArg;
|
|
RHDFUNC(rhdPtr);
|
|
|
|
if (rhdPtr->ChipSet < RHD_R600) {
|
|
RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS,
|
|
GET_DEFAULT_ENGINE_CLOCK, &atomBiosArg);
|
|
return (0x7f << 8)
|
|
+ (atomBiosArg.val / (4 * 0x7f * TARGET_HW_I2C_CLOCK));
|
|
} else {
|
|
RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS,
|
|
GET_REF_CLOCK, &atomBiosArg);
|
|
return (atomBiosArg.val / TARGET_HW_I2C_CLOCK);
|
|
}
|
|
#else
|
|
RHDFUNC(rhdPtr);
|
|
|
|
if (rhdPtr->ChipSet < RHD_R600) {
|
|
return (0x7f << 8)
|
|
+ (DEFAULT_ENGINE_CLOCK) / (4 * 0x7f * TARGET_HW_I2C_CLOCK);
|
|
} else {
|
|
return (DEFAULT_ENGINE_CLOCK / TARGET_HW_I2C_CLOCK);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static Bool
|
|
rhdI2CAddress(I2CDevPtr d, I2CSlaveAddr addr)
|
|
{
|
|
d->SlaveAddr = addr;
|
|
return xf86I2CWriteRead(d, NULL, 0, NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* This stub is needed to keep xf86I2CProbeAddress() happy.
|
|
*/
|
|
static void
|
|
rhdI2CStop(I2CDevPtr d)
|
|
{
|
|
}
|
|
|
|
static I2CBusPtr *
|
|
rhdInitI2C(int scrnIndex)
|
|
{
|
|
int i;
|
|
rhdI2CPtr I2C;
|
|
I2CBusPtr I2CPtr = NULL;
|
|
RHDPtr rhdPtr = RHDPTR(xf86Screens[scrnIndex]);
|
|
I2CBusPtr *I2CList;
|
|
int numLines = (rhdPtr->ChipSet < RHD_R600) ? 3 : I2C_LINES;
|
|
CARD16 prescale = rhdGetI2CPrescale(rhdPtr);
|
|
|
|
RHDFUNCI(scrnIndex);
|
|
|
|
if (!(I2CList = xcalloc(I2C_LINES, sizeof(I2CBusPtr)))) {
|
|
xf86DrvMsg(scrnIndex, X_ERROR,
|
|
"%s: Out of memory.\n",__func__);
|
|
}
|
|
/* We have 4 I2C lines */
|
|
for (i = 0; i < numLines; i++) {
|
|
if (!(I2C = xcalloc(sizeof(rhdI2CRec),1))) {
|
|
xf86DrvMsg(scrnIndex, X_ERROR,
|
|
"%s: Out of memory.\n",__func__);
|
|
goto error;
|
|
}
|
|
I2C->scrnIndex = scrnIndex;
|
|
/*
|
|
* This is a value that has been found to work on many cards.
|
|
* It nees to be replaced by the proper calculation formula
|
|
* once this is available.
|
|
*/
|
|
I2C->prescale = prescale;
|
|
xf86DrvMsgVerb(scrnIndex, X_INFO, 5, "I2C clock prescale value: %x\n",I2C->prescale);
|
|
I2C->line = i;
|
|
if (!(I2CPtr = xf86CreateI2CBusRec())) {
|
|
xf86DrvMsg(scrnIndex, X_ERROR,
|
|
"Cannot allocate I2C BusRec.\n");
|
|
xfree(I2C);
|
|
goto error;
|
|
}
|
|
I2CPtr->DriverPrivate.ptr = I2C;
|
|
if (!(I2CPtr->BusName = xalloc(18))) {
|
|
xf86DrvMsg(scrnIndex, X_ERROR,
|
|
"%s: Cannot allocate memory.\n",__func__);
|
|
xfree(I2C);
|
|
xf86DestroyI2CBusRec(I2CPtr, TRUE, FALSE);
|
|
goto error;
|
|
}
|
|
xf86snprintf(I2CPtr->BusName,17,"RHD I2C line %1.1i",i);
|
|
I2CPtr->scrnIndex = scrnIndex;
|
|
if (rhdPtr->ChipSet < RHD_RS690)
|
|
I2CPtr->I2CWriteRead = rhd5xxWriteRead;
|
|
else if (rhdPtr->ChipSet < RHD_R600)
|
|
I2CPtr->I2CWriteRead = rhdRS69WriteRead;
|
|
else
|
|
I2CPtr->I2CWriteRead = rhd6xxWriteRead;
|
|
I2CPtr->I2CAddress = rhdI2CAddress;
|
|
I2CPtr->I2CStop = rhdI2CStop;
|
|
|
|
if (!(xf86I2CBusInit(I2CPtr))) {
|
|
xf86DrvMsg(scrnIndex, X_ERROR,
|
|
"I2C BusInit failed for bus %i\n",i);
|
|
xfree(I2CPtr->BusName);
|
|
xfree(I2C);
|
|
xf86DestroyI2CBusRec(I2CPtr, TRUE, FALSE);
|
|
goto error;
|
|
}
|
|
I2CList[i] = I2CPtr;
|
|
}
|
|
return I2CList;
|
|
error:
|
|
rhdTearDownI2C(I2CList);
|
|
return NULL;
|
|
}
|
|
|
|
RHDI2CResult
|
|
rhdI2CProbeAddress(int scrnIndex, I2CBusPtr *I2CList,
|
|
int line, CARD8 slave)
|
|
{
|
|
I2CDevPtr dev;
|
|
int ret = FALSE;
|
|
char *name = "I2CProbe";
|
|
|
|
if (line >= I2C_LINES || !I2CList[line])
|
|
return RHD_I2C_NOLINE;
|
|
|
|
if ((dev = xf86CreateI2CDevRec())) {
|
|
dev->SlaveAddr = slave & 0xFE;
|
|
dev->DevName = name;
|
|
dev->pI2CBus = I2CList[line];
|
|
|
|
if (xf86I2CDevInit(dev))
|
|
ret = xf86I2CWriteRead(dev, NULL, 0, NULL, 0);
|
|
|
|
xf86DestroyI2CDevRec(dev, TRUE);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RHDI2CResult
|
|
RHDI2CFunc(int scrnIndex, I2CBusPtr *I2CList, RHDi2cFunc func,
|
|
RHDI2CDataArgPtr datap)
|
|
{
|
|
RHDFUNCI(scrnIndex);
|
|
|
|
if (func == RHD_I2C_INIT) {
|
|
if (!(datap->I2CBusList = rhdInitI2C(scrnIndex)))
|
|
return RHD_I2C_FAILED;
|
|
else
|
|
return RHD_I2C_SUCCESS;
|
|
}
|
|
if (func == RHD_I2C_DDC) {
|
|
if (datap->i >= I2C_LINES || !I2CList[datap->i])
|
|
return RHD_I2C_NOLINE;
|
|
|
|
datap->monitor = xf86DoEDID_DDC2(scrnIndex, I2CList[datap->i]);
|
|
return RHD_I2C_SUCCESS;
|
|
}
|
|
if (func == RHD_I2C_PROBE_ADDR) {
|
|
return rhdI2CProbeAddress(scrnIndex, I2CList,
|
|
datap->target.line,
|
|
datap->target.slave);
|
|
}
|
|
if (func == RHD_I2C_GETBUS) {
|
|
if (datap->i >= I2C_LINES || !I2CList[datap->i])
|
|
return RHD_I2C_NOLINE;
|
|
|
|
datap->i2cBusPtr = I2CList[datap->i];
|
|
return RHD_I2C_SUCCESS;
|
|
}
|
|
if (func == RHD_I2C_TEARDOWN) {
|
|
if (I2CList)
|
|
rhdTearDownI2C(I2CList);
|
|
return RHD_I2C_SUCCESS;
|
|
}
|
|
return RHD_I2C_FAILED;
|
|
}
|
|
|