2008-12-13 08:51:28 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2007, 2008 Egbert Eich <eich@novell.com>
|
|
|
|
* Copyright 2007, 2008 Luc Verhaegen <lverhaegen@novell.com>
|
|
|
|
* Copyright 2007, 2008 Matthias Hopf <mhopf@novell.com>
|
|
|
|
* Copyright 2007, 2008 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"
|
|
|
|
#if HAVE_XF86_ANSIC_H
|
|
|
|
# include "xf86_ansic.h"
|
|
|
|
#else
|
|
|
|
# include <unistd.h>
|
|
|
|
# include <stdio.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "rhd.h"
|
|
|
|
#include "edid.h"
|
|
|
|
#include "rhd_i2c.h"
|
|
|
|
#include "xf86DDC.h"
|
|
|
|
|
|
|
|
#include "rhd_regs.h"
|
|
|
|
|
|
|
|
#ifdef ATOM_BIOS
|
|
|
|
#include "rhd_atombios.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define MAX_I2C_LINES 6
|
|
|
|
|
|
|
|
#define RHD_I2C_STATUS_LOOPS 5000
|
|
|
|
|
|
|
|
enum rhdDDClines {
|
|
|
|
rhdDdc1data = 0,
|
|
|
|
rhdDdc2data = 2,
|
|
|
|
rhdDdc3data = 4,
|
|
|
|
rhdDdc4data = 6, /* arbirarily choosen */
|
|
|
|
rhdVIP_DOUT_scl = 0x41,
|
|
|
|
rhdDvoData12 = 0x28,
|
|
|
|
rhdDdc5data = 0x48,
|
|
|
|
rhdDdc6data = 0x4a,
|
|
|
|
rhdDdc1clk = 1,
|
|
|
|
rhdDdc2clk = 3,
|
|
|
|
rhdDdc3clk = 5,
|
|
|
|
rhdDdc4clk = 7, /* arbirarily choosen */
|
|
|
|
rhdVIP_DOUTvipclk = 0x42,
|
|
|
|
rhdDvoData13 = 0x29,
|
|
|
|
rhdDdc5clk = 0x49,
|
|
|
|
rhdDdc6clk = 0x4b,
|
|
|
|
rhdDdcUnknown
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _rhdI2CRec
|
|
|
|
{
|
|
|
|
CARD16 prescale;
|
|
|
|
union {
|
|
|
|
CARD8 line;
|
|
|
|
struct i2cGpio {
|
|
|
|
enum rhdDDClines Sda;
|
|
|
|
enum rhdDDClines Scl;
|
|
|
|
CARD32 SdaReg;
|
|
|
|
CARD32 SclReg;
|
|
|
|
} Gpio;
|
|
|
|
} u;
|
|
|
|
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_ABORTED = (0x1 << 4),
|
|
|
|
RS69_DC_I2C_SW_TIMEOUT = (0x1 << 5),
|
|
|
|
RS69_DC_I2C_SW_INTERRUPTED= (0x1 << 6),
|
|
|
|
RS69_DC_I2C_SW_BUFFER_OVERFLOW= (0x1 << 7),
|
|
|
|
RS69_DC_I2C_SW_STOPPED_ON_NACK = (0x1 << 8),
|
|
|
|
RS69_DC_I2C_SW_NACK0 = (0x1 << 12),
|
|
|
|
RS69_DC_I2C_SW_NACK1 = (0x1 << 13)
|
|
|
|
};
|
|
|
|
|
|
|
|
/* RV620 */
|
|
|
|
enum rv620I2CBits {
|
|
|
|
/* GENERIC_I2C_CONTROL */
|
|
|
|
RV62_DC_I2C_GO = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_GO = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_SOFT_RESET = (0x1 << 1),
|
|
|
|
RV62_GENERIC_I2C_SEND_RESET = (0x1 << 2),
|
|
|
|
/* GENERIC_I2C_INTERRUPT_CONTROL */
|
|
|
|
RV62_GENERIC_I2C_DONE_INT = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_DONE_ACK = (0x1 << 1),
|
|
|
|
RV62_GENERIC_I2C_DONE_MASK = (0x1 << 2),
|
|
|
|
/* GENERIC_I2C_STATUS */
|
|
|
|
RV62_GENERIC_I2C_STATUS_BIT = (0xf << 0),
|
|
|
|
RV62_GENERIC_I2C_DONE = (0x1 << 4),
|
|
|
|
RV62_GENERIC_I2C_ABORTED = (0x1 << 5),
|
|
|
|
RV62_GENERIC_I2C_TIMEOUT = (0x1 << 6),
|
|
|
|
RV62_GENERIC_I2C_STOPPED_ON_NACK = (0x1 << 9),
|
|
|
|
RV62_GENERIC_I2C_NACK = (0x1 << 10),
|
|
|
|
/* GENERIC_I2C_SPEED */
|
|
|
|
RV62_GENERIC_I2C_THRESHOLD = (0x3 << 0),
|
|
|
|
RV62_GENERIC_I2C_DISABLE_FILTER_DURING_STALL = (0x1 << 4),
|
|
|
|
RV62_GENERIC_I2C_PRESCALE = (0xffff << 16),
|
|
|
|
/* GENERIC_I2C_SETUP */
|
|
|
|
RV62_GENERIC_I2C_DATA_DRIVE_EN = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_DATA_DRIVE_SEL = (0x1 << 1),
|
|
|
|
RV62_GENERIC_I2C_CLK_DRIVE_EN = (0x1 << 7),
|
|
|
|
RV62_GENERIC_I2C_INTRA_BYTE_DELAY = (0xff << 8),
|
|
|
|
RV62_GENERIC_I2C_TIME_LIMIT = (0xff << 24),
|
|
|
|
/* GENERIC_I2C_TRANSACTION */
|
|
|
|
RV62_GENERIC_I2C_RW = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_STOP_ON_NACK = (0x1 << 8),
|
|
|
|
RV62_GENERIC_I2C_ACK_ON_READ = (0x1 << 9),
|
|
|
|
RV62_GENERIC_I2C_START = (0x1 << 12),
|
|
|
|
RV62_GENERIC_I2C_STOP = (0x1 << 13),
|
|
|
|
RV62_GENERIC_I2C_COUNT = (0xf << 16),
|
|
|
|
/* GENERIC_I2C_DATA */
|
|
|
|
RV62_GENERIC_I2C_DATA_RW = (0x1 << 0),
|
|
|
|
RV62_GENERIC_I2C_DATA_BIT = (0xff << 8),
|
|
|
|
RV62_GENERIC_I2C_INDEX = (0xf << 16),
|
|
|
|
RV62_GENERIC_I2C_INDEX_WRITE = (0x1 << 31),
|
|
|
|
/* GENERIC_I2C_PIN_SELECTION */
|
|
|
|
RV62_GENERIC_I2C_SCL_PIN_SEL_SHIFT = 0,
|
|
|
|
RV62_GENERIC_I2C_SCL_PIN_SEL = (0x7f << RV62_GENERIC_I2C_SCL_PIN_SEL_SHIFT),
|
|
|
|
RV62_GENERIC_I2C_SDA_PIN_SEL_SHIFT = 8,
|
|
|
|
RV62_GENERIC_I2C_SDA_PIN_SEL = (0x7f << RV62_GENERIC_I2C_SDA_PIN_SEL_SHIFT)
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static enum rhdDDClines
|
|
|
|
getDDCLineFromGPIO(int scrnIndex, CARD32 gpio, int shift)
|
|
|
|
{
|
|
|
|
switch (gpio) {
|
|
|
|
case 0x1f90:
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc1clk; /* ddc1 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdDdc1data; /* ddc1 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1f94: /* ddc2 */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc2clk; /* ddc2 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdDdc2data; /* ddc2 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1f98: /* ddc3 */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc3clk; /* ddc3 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdDdc3data; /* ddc3 data */
|
|
|
|
}
|
|
|
|
case 0x1f80: /* ddc4 - on r6xx */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc4clk; /* ddc4 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdDdc4data; /* ddc4 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1f88: /* ddc5 */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdVIP_DOUTvipclk; /* ddc5 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdVIP_DOUT_scl; /* ddc5 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1fda: /* ddc6 */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDvoData13; /* ddc6 clk */
|
|
|
|
case 1:
|
|
|
|
return rhdDvoData12; /* ddc6 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1fc4:
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc5clk;
|
|
|
|
case 8:
|
|
|
|
return rhdDdc5data;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x1fe8: /* ddc6 */
|
|
|
|
switch (shift) {
|
|
|
|
case 0:
|
|
|
|
return rhdDdc6clk; /* ddc6 clk */
|
|
|
|
case 8:
|
|
|
|
return rhdDdc6data; /* ddc6 data */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"%s: Failed to match GPIO 0x%04X.%d with a known DDC line\n",
|
|
|
|
__func__, (unsigned int) gpio, shift);
|
|
|
|
return rhdDdcUnknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static Bool
|
|
|
|
rhdI2CGetDataClkLines(RHDPtr rhdPtr, int line,
|
|
|
|
enum rhdDDClines *scl, enum rhdDDClines *sda,
|
|
|
|
CARD32 *sda_reg, CARD32 *scl_reg)
|
|
|
|
{
|
|
|
|
#ifdef ATOM_BIOS
|
|
|
|
AtomBiosResult result;
|
|
|
|
AtomBiosArgRec data;
|
|
|
|
|
|
|
|
/* scl register */
|
|
|
|
data.val = line & 0x0f;
|
|
|
|
result = RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
ATOM_GPIO_I2C_CLK_MASK, &data);
|
|
|
|
if (result != ATOM_SUCCESS)
|
|
|
|
return FALSE;
|
|
|
|
*scl_reg = data.val;
|
|
|
|
|
|
|
|
/* scl DDC line */
|
|
|
|
data.val = line & 0x0f;
|
|
|
|
result = RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
ATOM_GPIO_I2C_CLK_MASK_SHIFT, &data);
|
|
|
|
if (result != ATOM_SUCCESS)
|
|
|
|
return FALSE;
|
|
|
|
*scl = getDDCLineFromGPIO(rhdPtr->scrnIndex, *scl_reg, data.val);
|
|
|
|
|
|
|
|
/* sda register */
|
|
|
|
data.val = line & 0x0f;
|
|
|
|
result = RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
ATOM_GPIO_I2C_DATA_MASK, &data);
|
|
|
|
if (result != ATOM_SUCCESS)
|
|
|
|
return FALSE;
|
|
|
|
*sda_reg = data.val;
|
|
|
|
|
|
|
|
/* sda DDC line */
|
|
|
|
data.val = line & 0x0f;
|
|
|
|
result = RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
ATOM_GPIO_I2C_DATA_MASK_SHIFT, &data);
|
|
|
|
if (result != ATOM_SUCCESS)
|
|
|
|
return FALSE;
|
|
|
|
*sda = getDDCLineFromGPIO(rhdPtr->scrnIndex, *sda_reg, data.val);
|
|
|
|
|
|
|
|
if ((*scl == rhdDdcUnknown) || (*sda == rhdDdcUnknown)) {
|
|
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING,
|
|
|
|
"%s: failed to map gpio lines for DDC line %d\n",
|
|
|
|
__func__, line);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
#else /* ATOM_BIOS */
|
|
|
|
return FALSE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* R5xx */
|
|
|
|
static Bool
|
|
|
|
rhd5xxI2CSetupStatus(I2CBusPtr I2CPtr, int line)
|
|
|
|
{
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
line &= 0xf;
|
|
|
|
|
|
|
|
|
|
|
|
switch (line) {
|
|
|
|
case 0:
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC1_MASK, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC1_A, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC1_EN, 0x0, 0xffff);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC2_MASK, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC2_A, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R6_DC_GPIO_DDC2_EN, 0x0, 0xffff);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC3_MASK, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC3_A, 0x0, 0xffff);
|
|
|
|
RHDRegMask(I2CPtr, R5_DC_GPIO_DDC3_EN, 0x0, 0xffff);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
xf86DrvMsg(I2CPtr->scrnIndex,X_ERROR,
|
|
|
|
"%s: Trying to initialize non-existent I2C line: %i\n",
|
|
|
|
__func__,line);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
rhd5xxI2CStatus(I2CBusPtr I2CPtr)
|
|
|
|
{
|
|
|
|
int count = RHD_I2C_STATUS_LOOPS;
|
|
|
|
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, int line, I2CByte *WriteBuffer,
|
|
|
|
int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
|
|
{
|
|
|
|
I2CSlaveAddr slave = i2cDevPtr->SlaveAddr;
|
|
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
|
|
rhdI2CPtr I2C = (rhdI2CPtr)(I2CPtr->DriverPrivate.ptr);
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (!RHDRegRead(I2CPtr, R5_DC_I2C_ARBITRATION) & R5_DC_I2C_SW_CAN_USE_I2C) {
|
|
|
|
RHDDebug(I2CPtr->scrnIndex, "%s SW cannot use I2C line %i\n",__func__,line);
|
|
|
|
ret = FALSE;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
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 (ret && (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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
|
|
int ddc_line;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
ddc_line = ((rhdI2CPtr)(I2CPtr->DriverPrivate.ptr))->u.line;
|
|
|
|
|
|
|
|
rhd5xxI2CSetupStatus(I2CPtr, ddc_line);
|
|
|
|
|
|
|
|
if (nRead > 15) {
|
|
|
|
I2CByte offset = *WriteBuffer;
|
|
|
|
while (nRead) {
|
|
|
|
int n = nRead > 15 ? 15 : nRead;
|
|
|
|
if (!rhd5xxWriteReadChunk(i2cDevPtr, ddc_line, &offset, 1, ReadBuffer, n))
|
|
|
|
return FALSE;
|
|
|
|
ReadBuffer += n;
|
|
|
|
nRead -= n;
|
|
|
|
offset += n;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} else
|
|
|
|
return rhd5xxWriteReadChunk(i2cDevPtr, ddc_line, WriteBuffer, nWrite,
|
|
|
|
ReadBuffer, nRead);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RS690 */
|
|
|
|
static Bool
|
|
|
|
rhdRS69I2CStatus(I2CBusPtr I2CPtr)
|
|
|
|
{
|
|
|
|
volatile CARD32 val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
for (i = 0; i < RHD_I2C_STATUS_LOOPS; i++) {
|
|
|
|
usleep(10);
|
|
|
|
|
|
|
|
val = RHDRegRead(I2CPtr, RS69_DC_I2C_SW_STATUS);
|
|
|
|
|
|
|
|
RHDDebugVerb(I2CPtr->scrnIndex, 1, "SW_STATUS: 0x%x %i\n",
|
|
|
|
(unsigned int) val, i);
|
|
|
|
|
|
|
|
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 ((i == RHD_I2C_STATUS_LOOPS) ||
|
|
|
|
(val & (RS69_DC_I2C_SW_ABORTED | RS69_DC_I2C_SW_TIMEOUT |
|
|
|
|
RS69_DC_I2C_SW_INTERRUPTED | RS69_DC_I2C_SW_BUFFER_OVERFLOW |
|
|
|
|
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, enum rhdDDClines sda, enum rhdDDClines scl, int prescale)
|
|
|
|
{
|
|
|
|
CARD32 clk_pin, data_pin;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
switch (sda) {
|
|
|
|
case rhdDdc1data:
|
|
|
|
data_pin = 0;
|
|
|
|
break;
|
|
|
|
case rhdDdc2data:
|
|
|
|
data_pin = 1;
|
|
|
|
break;
|
|
|
|
case rhdDdc3data:
|
|
|
|
data_pin = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
switch (scl) {
|
|
|
|
case rhdDdc1data:
|
|
|
|
clk_pin = 4;
|
|
|
|
break;
|
|
|
|
case rhdDdc2data:
|
|
|
|
clk_pin = 5;
|
|
|
|
break;
|
|
|
|
case rhdDdc3data:
|
|
|
|
clk_pin = 6;
|
|
|
|
break;
|
|
|
|
case rhdDdc1clk:
|
|
|
|
clk_pin = 0;
|
|
|
|
break;
|
|
|
|
case rhdDdc2clk:
|
|
|
|
clk_pin = 1;
|
|
|
|
break;
|
|
|
|
case rhdDdc3clk:
|
|
|
|
clk_pin = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
RHDRegMask(I2CPtr, 0x28, 0x200, 0x200);
|
|
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_UNKNOWN_1, prescale << 16 | 0x2, 0xffff00ff);
|
|
|
|
RHDRegWrite(I2CPtr, RS69_DC_I2C_DDC_SETUP_Q, 0x30000000);
|
|
|
|
RHDRegMask(I2CPtr, RS69_DC_I2C_CONTROL, ((data_pin & 0x3) << 16) | (clk_pin << 8), 0xffff00);
|
|
|
|
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;
|
|
|
|
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, I2C->u.Gpio.Sda, I2C->u.Gpio.Scl, 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)
|
|
|
|
{
|
|
|
|
volatile CARD32 val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
for (i = 0; i < RHD_I2C_STATUS_LOOPS; i++) {
|
|
|
|
usleep(10);
|
|
|
|
|
|
|
|
val = RHDRegRead(I2CPtr, R6_DC_I2C_SW_STATUS);
|
|
|
|
|
|
|
|
RHDDebugVerb(I2CPtr->scrnIndex, 1, "SW_STATUS: 0x%x %i\n",
|
|
|
|
(unsigned int) val, i);
|
|
|
|
|
|
|
|
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 ((i == RHD_I2C_STATUS_LOOPS) ||
|
|
|
|
(val & (R6_DC_I2C_SW_ABORTED | R6_DC_I2C_SW_TIMEOUT |
|
|
|
|
R6_DC_I2C_SW_INTERRUPTED | R6_DC_I2C_SW_BUFFER_OVERFLOW |
|
|
|
|
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;
|
|
|
|
CARD32 ddc_line = I2C->u.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, ddc_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RV620 */
|
|
|
|
static Bool
|
|
|
|
rhdRV620I2CStatus(I2CBusPtr I2CPtr)
|
|
|
|
{
|
|
|
|
volatile CARD32 val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
for (i = 0; i < RHD_I2C_STATUS_LOOPS; i++) {
|
|
|
|
usleep(10);
|
|
|
|
|
|
|
|
val = RHDRegRead(I2CPtr, RV62_GENERIC_I2C_STATUS);
|
|
|
|
|
|
|
|
RHDDebugVerb(I2CPtr->scrnIndex, 1,
|
|
|
|
"SW_STATUS: 0x%x %i\n", (unsigned int) val, i);
|
|
|
|
if (val & RV62_GENERIC_I2C_DONE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_INTERRUPT_CONTROL, 0x2, 0xff);
|
|
|
|
|
|
|
|
if ((i == RHD_I2C_STATUS_LOOPS) ||
|
|
|
|
(val & (RV62_GENERIC_I2C_STOPPED_ON_NACK | RV62_GENERIC_I2C_NACK |
|
|
|
|
RV62_GENERIC_I2C_ABORTED | RV62_GENERIC_I2C_TIMEOUT)))
|
|
|
|
return FALSE; /* 2 */
|
|
|
|
|
|
|
|
return TRUE; /* 1 */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static Bool
|
|
|
|
rhdRV620I2CSetupStatus(I2CBusPtr I2CPtr, struct i2cGpio *Gpio, int prescale)
|
|
|
|
{
|
|
|
|
CARD32 reg_7d9c = 0; /* 0 is invalid */
|
|
|
|
CARD32 scl_reg;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
scl_reg = Gpio->SclReg;
|
|
|
|
reg_7d9c = (Gpio->Scl << RV62_GENERIC_I2C_SCL_PIN_SEL_SHIFT)
|
|
|
|
| (Gpio->Sda << RV62_GENERIC_I2C_SDA_PIN_SEL_SHIFT);
|
|
|
|
|
|
|
|
scl_reg = Gpio->SclReg;
|
|
|
|
/* Don't understand this yet */
|
|
|
|
if (scl_reg == 0x1fda)
|
|
|
|
scl_reg = 0x1f90;
|
|
|
|
|
|
|
|
RHDRegWrite(I2CPtr, scl_reg << 2, 0);
|
|
|
|
|
|
|
|
RHDRegWrite(I2CPtr, RV62_GENERIC_I2C_PIN_SELECTION, reg_7d9c);
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_SPEED,
|
|
|
|
(prescale & 0xffff) << 16 | 0x02, 0xffff00ff);
|
|
|
|
RHDRegWrite(I2CPtr, RV62_GENERIC_I2C_SETUP, 0x30000000);
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_INTERRUPT_CONTROL,
|
|
|
|
RV62_GENERIC_I2C_DONE_ACK, RV62_GENERIC_I2C_DONE_ACK);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
rhdRV620Transaction(I2CDevPtr i2cDevPtr, Bool Write, I2CByte *Buffer, int count)
|
|
|
|
{
|
|
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
|
|
I2CSlaveAddr slave = i2cDevPtr->SlaveAddr;
|
|
|
|
Bool Start = TRUE;
|
|
|
|
|
|
|
|
RHDFUNC(I2CPtr);
|
|
|
|
|
|
|
|
#define MAX 8
|
|
|
|
|
|
|
|
while (count > 0 || (Write && Start)) {
|
|
|
|
int num;
|
|
|
|
int idx = 0;
|
|
|
|
CARD32 data = 0;
|
|
|
|
|
|
|
|
if (count > MAX) {
|
|
|
|
num = MAX;
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_TRANSACTION,
|
|
|
|
(MAX - (((Start) ? 0 : 1))) << 16
|
|
|
|
| RV62_GENERIC_I2C_STOP_ON_NACK
|
|
|
|
| RV62_GENERIC_I2C_ACK_ON_READ
|
|
|
|
| (Start ? RV62_GENERIC_I2C_START : 0)
|
|
|
|
| (!Write ? RV62_GENERIC_I2C_RW : 0 ),
|
|
|
|
0xFFFFFF);
|
|
|
|
} else {
|
|
|
|
num = count;
|
|
|
|
data = ( count - (((Start) ? 0 : 1)) ) << 16
|
|
|
|
| RV62_GENERIC_I2C_STOP_ON_NACK
|
|
|
|
| RV62_GENERIC_I2C_STOP
|
|
|
|
| (Start ? RV62_GENERIC_I2C_START : 0)
|
|
|
|
| (!Write ? RV62_GENERIC_I2C_RW : 0);
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_TRANSACTION,
|
|
|
|
data,
|
|
|
|
0xFFFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Start) {
|
|
|
|
data = RV62_GENERIC_I2C_INDEX_WRITE
|
|
|
|
| (((slave & 0xfe) | ( Write ? 0 : 1)) << 8)
|
|
|
|
| (idx++ << 16);
|
|
|
|
RHDRegWrite(I2CPtr, RV62_GENERIC_I2C_DATA, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Write) {
|
|
|
|
while (num--) {
|
|
|
|
data = RV62_GENERIC_I2C_INDEX_WRITE
|
|
|
|
| (idx++ << 16)
|
|
|
|
| *(Buffer++) << 8;
|
|
|
|
RHDRegWrite(I2CPtr, RV62_GENERIC_I2C_DATA, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_CONTROL,
|
|
|
|
RV62_GENERIC_I2C_GO, RV62_GENERIC_I2C_GO);
|
|
|
|
if (!rhdRV620I2CStatus(I2CPtr))
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
RHDRegMask(I2CPtr, RV62_GENERIC_I2C_CONTROL,
|
|
|
|
RV62_GENERIC_I2C_GO, RV62_GENERIC_I2C_GO);
|
|
|
|
if (!rhdRV620I2CStatus(I2CPtr))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
RHDRegWrite(I2CPtr, RV62_GENERIC_I2C_DATA,
|
|
|
|
RV62_GENERIC_I2C_INDEX_WRITE
|
|
|
|
| (idx++ << 16)
|
|
|
|
| RV62_GENERIC_I2C_RW);
|
|
|
|
|
|
|
|
while (num--) {
|
|
|
|
data = RHDRegRead(I2CPtr, RV62_GENERIC_I2C_DATA);
|
|
|
|
*(Buffer++) = (CARD8)((data >> 8) & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Start = FALSE;
|
|
|
|
count -= MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
rhdRV620WriteRead(I2CDevPtr i2cDevPtr, I2CByte *WriteBuffer, int nWrite, I2CByte *ReadBuffer, int nRead)
|
|
|
|
{
|
|
|
|
I2CBusPtr I2CPtr = i2cDevPtr->pI2CBus;
|
|
|
|
rhdI2CPtr I2C = (rhdI2CPtr)I2CPtr->DriverPrivate.ptr;
|
|
|
|
int prescale = I2C->prescale;
|
|
|
|
|
|
|
|
RHDFUNC(I2C);
|
|
|
|
|
|
|
|
rhdRV620I2CSetupStatus(I2CPtr, &I2C->u.Gpio, prescale);
|
|
|
|
|
|
|
|
if (nWrite || !nRead)
|
|
|
|
if (!rhdRV620Transaction(i2cDevPtr, TRUE, WriteBuffer, nWrite))
|
|
|
|
return FALSE;
|
|
|
|
if (nRead)
|
|
|
|
if (!rhdRV620Transaction(i2cDevPtr, FALSE, ReadBuffer, nRead))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rhdTearDownI2C(I2CBusPtr *I2C)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* xf86I2CGetScreenBuses() is
|
|
|
|
* broken in older server versions.
|
|
|
|
* So we cannot use it. How bad!
|
|
|
|
*/
|
2009-02-11 07:52:01 +01:00
|
|
|
for (i = 0; i < MAX_I2C_LINES; i++) {
|
2008-12-13 08:51:28 +01:00
|
|
|
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 453000 /* kHz (guessed) */
|
|
|
|
#define DEFAULT_REF_CLOCK 27000
|
|
|
|
|
|
|
|
static CARD32
|
|
|
|
rhdGetI2CPrescale(RHDPtr rhdPtr)
|
|
|
|
{
|
|
|
|
#ifdef ATOM_BIOS
|
|
|
|
AtomBiosArgRec atomBiosArg;
|
|
|
|
RHDFUNC(rhdPtr);
|
|
|
|
|
|
|
|
if (rhdPtr->ChipSet < RHD_R600) {
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
GET_DEFAULT_ENGINE_CLOCK, &atomBiosArg)
|
|
|
|
== ATOM_SUCCESS)
|
|
|
|
return (0x7f << 8)
|
|
|
|
+ (atomBiosArg.val / (4 * 0x7f * TARGET_HW_I2C_CLOCK));
|
|
|
|
else
|
|
|
|
return (0x7f << 8)
|
|
|
|
+ (DEFAULT_ENGINE_CLOCK / (4 * 0x7f * TARGET_HW_I2C_CLOCK));
|
|
|
|
} else if (rhdPtr->ChipSet < RHD_RV620) {
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
GET_REF_CLOCK, &atomBiosArg) == ATOM_SUCCESS)
|
|
|
|
return (atomBiosArg.val / TARGET_HW_I2C_CLOCK);
|
|
|
|
else
|
|
|
|
return (DEFAULT_REF_CLOCK / TARGET_HW_I2C_CLOCK);
|
|
|
|
} else {
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
|
|
GET_REF_CLOCK, &atomBiosArg) == ATOM_SUCCESS)
|
|
|
|
return (atomBiosArg.val / (4 * TARGET_HW_I2C_CLOCK));
|
|
|
|
else
|
|
|
|
return (DEFAULT_REF_CLOCK / (4 * 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 if (rhdPtr->ChipSet < RHD_RV620) {
|
|
|
|
return (DEFAULT_REF_CLOCK / TARGET_HW_I2C_CLOCK);
|
|
|
|
} else
|
|
|
|
return (DEFAULT_REF_CLOCK / (4 * 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)scrnIndex;
|
|
|
|
I2CBusPtr *I2CList;
|
|
|
|
int numLines;
|
|
|
|
CARD16 prescale = rhdGetI2CPrescale(rhdPtr);
|
|
|
|
enum rhdDDClines sda = 0, scl = 0;
|
|
|
|
CARD32 scl_reg = 0, sda_reg = 0;
|
|
|
|
Bool valid;
|
|
|
|
|
|
|
|
RHDFUNCI(scrnIndex);
|
|
|
|
|
|
|
|
if (rhdPtr->ChipSet < RHD_RS600)
|
|
|
|
numLines = 3;
|
|
|
|
else if (rhdPtr->ChipSet < RHD_R600)
|
|
|
|
numLines = 4;
|
|
|
|
else if (rhdPtr->ChipSet < RHD_RV730)
|
|
|
|
numLines = 4;
|
|
|
|
else
|
|
|
|
numLines = MAX_I2C_LINES;
|
|
|
|
|
|
|
|
if (!(I2CList = xcalloc(MAX_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;
|
|
|
|
|
|
|
|
valid = rhdI2CGetDataClkLines(rhdPtr, i, &scl, &sda, &sda_reg, &scl_reg);
|
|
|
|
if (rhdPtr->ChipSet < RHD_RS600
|
|
|
|
|| (rhdPtr->ChipSet > RHD_RS740 && rhdPtr->ChipSet < RHD_RV620)) {
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
if (sda == rhdDdc1data && scl == rhdDdc1clk)
|
|
|
|
I2C->u.line = 0;
|
|
|
|
else if (sda == rhdDdc2data && scl == rhdDdc2clk)
|
|
|
|
I2C->u.line = 1;
|
|
|
|
else if (sda == rhdDdc3data && scl == rhdDdc3clk)
|
|
|
|
I2C->u.line = 2;
|
|
|
|
else if (rhdPtr->ChipSet > RHD_RS740 && sda == rhdDdc4data && scl == rhdDdc4clk)
|
|
|
|
I2C->u.line = 3; /* R6XX only */
|
|
|
|
else {
|
|
|
|
xf86DrvMsg(I2CPtr->scrnIndex, X_ERROR, "No DDC line found for index %i: scl=0x%2.2x sda=0x%2.2x\n",
|
|
|
|
i, scl, sda);
|
|
|
|
xfree(I2C);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else
|
|
|
|
I2C->u.line = i;
|
|
|
|
|
|
|
|
} else if (rhdPtr->ChipSet <= RHD_RS740) {
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
if (sda != rhdDdc1data && sda != rhdDdc2data && sda != rhdDdc3data) {
|
|
|
|
xf86DrvMsg(I2CPtr->scrnIndex, X_ERROR, "Invalid DDC CLK pin found: %i\n",
|
|
|
|
sda);
|
|
|
|
xfree(I2C);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (scl != rhdDdc1data && scl != rhdDdc2data && scl != rhdDdc3data
|
|
|
|
&& scl != rhdDdc1clk && scl != rhdDdc2clk && scl != rhdDdc3clk) {
|
|
|
|
xf86DrvMsg(I2CPtr->scrnIndex, X_ERROR, "Invalid DDC CLK pin found: %i\n",
|
|
|
|
scl);
|
|
|
|
xfree(I2C);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
I2C->u.Gpio.Sda = sda;
|
|
|
|
I2C->u.Gpio.Scl = scl;
|
|
|
|
I2C->u.Gpio.SdaReg = sda_reg;
|
|
|
|
I2C->u.Gpio.SclReg = scl_reg;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
xf86DrvMsg(I2CPtr->scrnIndex, X_ERROR, "Invalid ClkLine for DDC. "
|
|
|
|
"AtomBIOS reported wrong or AtomBIOS unavailable\n");
|
|
|
|
xfree(I2C);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
I2C->u.Gpio.Sda = sda;
|
|
|
|
I2C->u.Gpio.Scl = scl;
|
|
|
|
I2C->u.Gpio.SdaReg = sda_reg;
|
|
|
|
I2C->u.Gpio.SclReg = scl_reg;
|
|
|
|
} else {
|
|
|
|
CARD32 gpioReg[] = { 0x1f90, 0x1f94, 0x1f98 };
|
|
|
|
enum rhdDDClines sdaList[] = { rhdDdc1data, rhdDdc2data, rhdDdc3data };
|
|
|
|
enum rhdDDClines sclList[] = { rhdDdc1clk, rhdDdc2clk, rhdDdc3clk };
|
|
|
|
if (i > 2) {
|
|
|
|
xfree(I2C);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
I2C->u.Gpio.Sda = sdaList[i];
|
|
|
|
I2C->u.Gpio.Scl = sclList[i];
|
|
|
|
I2C->u.Gpio.SclReg = I2C->u.Gpio.SdaReg = gpioReg[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
snprintf(I2CPtr->BusName,17,"RHD I2C line %1.1i",i);
|
|
|
|
I2CPtr->scrnIndex = scrnIndex;
|
|
|
|
if (rhdPtr->ChipSet < RHD_RS600)
|
|
|
|
I2CPtr->I2CWriteRead = rhd5xxWriteRead;
|
|
|
|
else if (rhdPtr->ChipSet >= RHD_RS600 && rhdPtr->ChipSet <= RHD_RS740)
|
|
|
|
I2CPtr->I2CWriteRead = rhdRS69WriteRead;
|
|
|
|
else if (rhdPtr->ChipSet < RHD_RV620)
|
|
|
|
I2CPtr->I2CWriteRead = rhd6xxWriteRead;
|
|
|
|
else
|
|
|
|
I2CPtr->I2CWriteRead = rhdRV620WriteRead;
|
|
|
|
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 I2CBusPtr, CARD8 slave)
|
|
|
|
{
|
|
|
|
I2CDevPtr dev;
|
|
|
|
char *name = "I2CProbe";
|
|
|
|
|
|
|
|
if ((dev = xf86CreateI2CDevRec())) {
|
|
|
|
dev->DevName = name;
|
|
|
|
dev->pI2CBus = I2CBusPtr;
|
|
|
|
|
|
|
|
if (xf86I2CDevInit(dev)) {
|
|
|
|
Bool ret;
|
|
|
|
|
|
|
|
dev->SlaveAddr = slave & 0xFE;
|
|
|
|
|
|
|
|
ret = xf86I2CWriteRead(dev, NULL, 0, NULL, 0);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
unsigned char offset = 0;
|
|
|
|
unsigned char buf[2];
|
|
|
|
|
|
|
|
/*
|
|
|
|
ASUS M2A-VM (R690) motherboards ACK all I2C slaves on the
|
|
|
|
HDMI line when the HDMI riser card is not installed.
|
|
|
|
We therefore need to read the first two bytes and check
|
|
|
|
if they are part of an I2C header.
|
|
|
|
*/
|
|
|
|
ret = xf86I2CWriteRead(dev, &offset, 1, buf, 2);
|
|
|
|
if (ret && (buf[0] != 0 || buf[1] != 0xff))
|
|
|
|
ret = FALSE;
|
|
|
|
}
|
|
|
|
xf86DestroyI2CDevRec(dev, TRUE);
|
|
|
|
|
|
|
|
return ret ? RHD_I2C_SUCCESS : RHD_I2C_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return RHD_I2C_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 >= MAX_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_LINE) {
|
|
|
|
|
|
|
|
if (datap->target.line >= MAX_I2C_LINES || !I2CList[datap->target.line])
|
|
|
|
return RHD_I2C_NOLINE;
|
|
|
|
return rhdI2CProbeAddress(scrnIndex, I2CList[datap->target.line], datap->target.slave);
|
|
|
|
}
|
|
|
|
if (func == RHD_I2C_PROBE_ADDR) {
|
|
|
|
return rhdI2CProbeAddress(scrnIndex, datap->probe.i2cBusPtr, datap->probe.slave);
|
|
|
|
}
|
|
|
|
if (func == RHD_I2C_GETBUS) {
|
|
|
|
if (datap->i >= MAX_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;
|
|
|
|
}
|
|
|
|
|