#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>

#include "radeon.h"
#include "radeon_reg.h"
#include "radeon_macros.h"

#include "xf86.h"

/* i2c stuff */
#include "xf86i2c.h"
#include "i2c_def.h"

/* ------ DDC ------ */

static void RADEONI2CGetBits(I2CBusPtr b, int *Clock, int *data)
{
    ScrnInfoPtr    pScrn      = xf86Screens[b->scrnIndex];
    RADEONInfoPtr  info       = RADEONPTR(pScrn);
    unsigned long  val;
    unsigned char *RADEONMMIO = info->MMIO;

    /* Get the result */
    val = INREG(info->DDCReg);

    *Clock = (val & RADEON_GPIO_Y_1) != 0;
    *data  = (val & RADEON_GPIO_Y_0) != 0;
}

static void RADEONI2CPutBits(I2CBusPtr b, int Clock, int data)
{
    ScrnInfoPtr    pScrn      = xf86Screens[b->scrnIndex];
    RADEONInfoPtr  info       = RADEONPTR(pScrn);
    unsigned long  val;
    unsigned char *RADEONMMIO = info->MMIO;

    val = INREG(info->DDCReg) & (CARD32)~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1);
    val |= (Clock ? 0:RADEON_GPIO_EN_1);
    val |= (data ? 0:RADEON_GPIO_EN_0);
    OUTREG(info->DDCReg, val);

    /* read back to improve reliability on some cards. */
    val = INREG(info->DDCReg);
}

Bool RADEONI2cInit(ScrnInfoPtr pScrn)
{
    RADEONInfoPtr  info = RADEONPTR(pScrn);

    info->pI2CBus = xf86CreateI2CBusRec();
    if (!info->pI2CBus) return FALSE;

    info->pI2CBus->BusName    = "DDC";
    info->pI2CBus->scrnIndex  = pScrn->scrnIndex;
    info->pI2CBus->I2CPutBits = RADEONI2CPutBits;
    info->pI2CBus->I2CGetBits = RADEONI2CGetBits;
    info->pI2CBus->AcknTimeout = 5;

#if 1
    /* langer timeouts as per the vesa spec */
    info->pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
    info->pI2CBus->StartTimeout = 550;
    info->pI2CBus->BitTimeout = 40;
    info->pI2CBus->ByteTimeout = 40;
    info->pI2CBus->AcknTimeout = 40;
#endif

    if (!xf86I2CBusInit(info->pI2CBus)) return FALSE;
    return TRUE;
}

/* ----------------- */

/* Wait for 10ms at the most for the I2C_GO register to drop. */
#define I2C_WAIT_FOR_GO() { \
    int i2ctries = 0; \
    RADEONWaitForIdleMMIO(pScrn); \
    write_mem_barrier(); \
    while (i2ctries < 10) { \
	reg = INREG8(info->i2c_info.i2c_cntl0_reg + 1); \
	if (!(reg & (RADEON_I2C_GO >> 8))) \
	    break; \
	if (reg & (RADEON_I2C_ABORT >> 8)) \
	    break; \
	usleep(1000); \
	i2ctries++; \
    } \
}

/* Wait, and dump the status in the 'status' register.  If we time out or
 * receive an abort signal, halt/restart the I2C bus and leave _ABORT in the
 * status register. */
#define I2C_WAIT_WITH_STATUS() { \
    I2C_WAIT_FOR_GO() \
    if (reg & ((RADEON_I2C_ABORT >> 8) | (RADEON_I2C_GO >> 8))) { \
	RADEON_I2C_Halt(pScrn); \
	status = RADEON_I2C_ABORT; \
    } \
    else \
	status = RADEON_I2C_WaitForAck(pScrn); \
}

/****************************************************************************
 *  I2C_WaitForAck (void)                                                   *
 *                                                                          *
 *  Function: polls the I2C status bits, waiting for an acknowledge or      *
 *            an error condition.                                           *
 *    Inputs: NONE                                                          *
 *   Outputs: I2C_DONE - the I2C transfer was completed                     *
 *            I2C_NACK - an NACK was received from the slave                *
 *            I2C_HALT - a timeout condition has occured                    *
 ****************************************************************************/
static CARD8 RADEON_I2C_WaitForAck (ScrnInfoPtr pScrn)
{
    CARD8 retval = 0;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;
    long counter = 0;

    usleep(1000);
    while (1) {
        RADEONWaitForIdleMMIO(pScrn); 
        retval = INREG8(info->i2c_info.i2c_cntl0_reg);
        if (retval & RADEON_I2C_HALT) {
            return (RADEON_I2C_HALT);
        }
        if (retval & RADEON_I2C_NACK) {
            return (RADEON_I2C_NACK);
        }
        if(retval & RADEON_I2C_DONE) {
            return RADEON_I2C_DONE;
        }       
        counter++;
	/* 50ms ought to be long enough. */
        if (counter > 50) {
             xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Timeout condition on Radeon i2c bus\n");
             return RADEON_I2C_HALT;
        }
	usleep(1000);
    }
}

static void RADEON_I2C_Halt (ScrnInfoPtr pScrn)
{
    RADEONInfoPtr info = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;
    CARD8    reg;

    /* reset status flags */
    RADEONWaitForIdleMMIO(pScrn);
    reg = INREG8 (info->i2c_info.i2c_cntl0_reg + 0) & ~(RADEON_I2C_DONE|RADEON_I2C_NACK|RADEON_I2C_HALT);
    OUTREG8(info->i2c_info.i2c_cntl0_reg + 0, reg);

    /* issue ABORT call */
    RADEONWaitForIdleMMIO(pScrn);
    reg = INREG8(info->i2c_info.i2c_cntl0_reg + 1) & 0xE7;
    OUTREG8(info->i2c_info.i2c_cntl0_reg + 1, (reg |((RADEON_I2C_GO|RADEON_I2C_ABORT) >> 8)));

    /* wait for GO bit to go low */
    I2C_WAIT_FOR_GO();
}


static Bool RADEONI2CWriteRead(I2CDevPtr d, I2CByte *WriteBuffer, int nWrite,
                            I2CByte *ReadBuffer, int nRead)
{
    int loop, status;
    CARD32 i2c_cntl_0, i2c_cntl_1;
    CARD8 reg;
    ScrnInfoPtr pScrn = xf86Screens[d->pI2CBus->scrnIndex];
    RADEONInfoPtr info = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;

    status = RADEON_I2C_DONE;

    RADEONWaitForIdleMMIO(pScrn);
    if (nWrite > 0){
        /*RADEONWaitForFifo(pScrn, 4+nWrite); */

        /* Clear the status bits of the I2C Controller */
        OUTREG(info->i2c_info.i2c_cntl0_reg, RADEON_I2C_DONE | RADEON_I2C_NACK |
					     RADEON_I2C_HALT | RADEON_I2C_SOFT_RST);

        /* Write the address into the buffer first */
        OUTREG(info->i2c_info.i2c_data_reg, (CARD32) (d->SlaveAddr) & ~(1));

        /* Write Value into the buffer */
        for (loop = 0; loop < nWrite; loop++) {
            OUTREG8(info->i2c_info.i2c_data_reg, WriteBuffer[loop]);
        }

        i2c_cntl_1 = (info->i2c_info.radeon_i2c_timing << 24) |
		     RADEON_I2C_EN | RADEON_I2C_SEL | nWrite;

	if (info->ChipFamily >= CHIP_FAMILY_R200) {
	    i2c_cntl_1 |= (1 << R200_I2C_ADDR_COUNT_SHIFT);
	} else {
	    i2c_cntl_1 |= (1 << RADEON_I2C_ADDR_COUNT_SHIFT);
	}

        OUTREG(info->i2c_info.i2c_cntl1_reg, i2c_cntl_1);
    
	i2c_cntl_0 = (info->i2c_info.radeon_N << 24) |
		     (info->i2c_info.radeon_M << 16) | 
                     RADEON_I2C_GO | RADEON_I2C_START |
		     ((nRead > 0) ? 0 : RADEON_I2C_STOP) |
		     RADEON_I2C_DRIVE_EN;

	OUTREG(info->i2c_info.i2c_cntl0_reg, i2c_cntl_0);
    
	I2C_WAIT_WITH_STATUS();

       	if (status != RADEON_I2C_DONE){
	    RADEON_I2C_Halt(pScrn);
            return FALSE;
	}
    }

    if (nRead > 0) {
        RADEONWaitForFifo(pScrn, 4+nRead);
    
        OUTREG(info->i2c_info.i2c_cntl0_reg, RADEON_I2C_DONE |
					     RADEON_I2C_NACK |
 					     RADEON_I2C_HALT |
 					     RADEON_I2C_SOFT_RST); 

        /* Write the address into the buffer first */
        OUTREG(info->i2c_info.i2c_data_reg, (CARD32) (d->SlaveAddr) | (1));

        i2c_cntl_1 = (info->i2c_info.radeon_i2c_timing << 24) |
		     RADEON_I2C_EN | RADEON_I2C_SEL | nRead;

	if (info->ChipFamily >= CHIP_FAMILY_R200) {
	    i2c_cntl_1 |= (1 << R200_I2C_ADDR_COUNT_SHIFT);
	} else {
	    i2c_cntl_1 |= (1 << RADEON_I2C_ADDR_COUNT_SHIFT);
	}

        OUTREG(info->i2c_info.i2c_cntl1_reg, i2c_cntl_1);
    
        i2c_cntl_0 = (info->i2c_info.radeon_N << 24) |
		     (info->i2c_info.radeon_M << 16) | 
                     RADEON_I2C_GO | RADEON_I2C_START |
		     RADEON_I2C_STOP | RADEON_I2C_DRIVE_EN |
		     RADEON_I2C_RECEIVE;

        OUTREG(info->i2c_info.i2c_cntl0_reg, i2c_cntl_0);
    
        I2C_WAIT_WITH_STATUS();
  
        /* Write Value into the buffer */
        for (loop = 0; loop < nRead; loop++) {
            RADEONWaitForFifo(pScrn, 1); /* not on R200 ??? */
            if ((status == RADEON_I2C_HALT) || (status == RADEON_I2C_NACK)) {
          	ReadBuffer[loop] = 0xff;
	    } else {
          	RADEONWaitForIdleMMIO(pScrn); /* not on R200 ??? */
          	ReadBuffer[loop] = INREG8(info->i2c_info.i2c_data_reg) & 0xff;
            }
        }
    }
    
    if (status != RADEON_I2C_DONE) {
        RADEON_I2C_Halt(pScrn);
        return FALSE;
    }

    return TRUE;
}

void RADEONResetI2C(ScrnInfoPtr pScrn)
{
    RADEONInfoPtr info = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;

    RADEONWaitForFifo(pScrn, 2);
    OUTREG8(info->i2c_info.i2c_cntl1_reg+2, ((RADEON_I2C_SEL | RADEON_I2C_EN)>>16));
    OUTREG8(info->i2c_info.i2c_cntl0_reg+0, (RADEON_I2C_DONE | RADEON_I2C_NACK |
					     RADEON_I2C_HALT | RADEON_I2C_SOFT_RST |
					     RADEON_I2C_DRIVE_EN | RADEON_I2C_DRIVE_SEL));
}

#if 0
static Bool RADEONProbeAddress(I2CBusPtr b, I2CSlaveAddr addr)
{
     I2CByte a;
     I2CDevRec d;
     
     d.DevName = "Probing";
     d.SlaveAddr = addr;
     d.pI2CBus = b;
     d.NextDev = NULL;
     
     return I2C_WriteRead(&d, NULL, 0, &a, 1);
}
#endif

/* probably need to call this when changing clocks */
void RADEONResetI2CTiming(ScrnInfoPtr pScrn) {
    double nm;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    RADEONPLLPtr  pll = &(info->pll);

    /* XXX FIXME */
#if 0
    switch (info->ChipFamily) {
	case CHIP_FAMILY_RV200:
            nm = (pll->reference_freq * 40000.0) / (1.0 * RADEON_I2C_CLOCK_FREQ);
	    break;
    	case CHIP_FAMILY_R300:
    	case CHIP_FAMILY_R200:
    	    if (info->MM_TABLE_valid && (RADEON_tuners[info->MM_TABLE.tuner_type & 0x1f].type == TUNER_TYPE_MT2032)){
                nm = (pll->reference_freq * 40000.0) / (4.0 * RADEON_I2C_CLOCK_FREQ);
	        break;
                } 
	default:
            nm = (pll->reference_freq * 10000.0) / (4.0 * RADEON_I2C_CLOCK_FREQ);
        }
#else
    nm = (pll->xclk * 40000.0) / (1.0 * RADEON_I2C_CLOCK_FREQ);         
#endif

    for (info->i2c_info.radeon_N = 1; info->i2c_info.radeon_N < 255; info->i2c_info.radeon_N++)
	if ((info->i2c_info.radeon_N * (info->i2c_info.radeon_N - 1)) > nm) break;
    info->i2c_info.radeon_M = info->i2c_info.radeon_N - 1;
    info->i2c_info.radeon_i2c_timing = 2 * info->i2c_info.radeon_N;
}

void RADEONInitI2C(ScrnInfoPtr pScrn)
{
    double nm;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    RADEONPLLPtr  pll = &(info->pll);

    info->i2c_info.pI2CBus = CreateI2CBusRec();
    info->i2c_info.pI2CBus->scrnIndex = pScrn->scrnIndex;
    info->i2c_info.pI2CBus->BusName = "Radeon i2c bus";
    info->i2c_info.pI2CBus->I2CWriteRead = RADEONI2CWriteRead;

    info->i2c_info.i2c_cntl0_reg = RADEON_I2C_CNTL_0; /* RADEON_DVI_I2C_CNTL_0 */
    info->i2c_info.i2c_cntl1_reg = RADEON_I2C_CNTL_1; /* RADEON_DVI_I2C_CNTL_1 */
    info->i2c_info.i2c_data_reg = RADEON_I2C_DATA; /* RADEON_DVI_I2C_DATA */

#if 0
    /* langer timeouts as per the vesa spec */
    info->i2c_info.pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
    info->i2c_info.pI2CBus->StartTimeout = 550;
    info->i2c_info.pI2CBus->BitTimeout = 40;
    info->i2c_info.pI2CBus->ByteTimeout = 40;
    info->i2c_info.pI2CBus->AcknTimeout = 40;
#endif

    if (!I2CBusInit(info->i2c_info.pI2CBus)) {
        xf86DrvMsg(pScrn->scrnIndex,X_ERROR, "Failed to register i2c bus\n");
    }

    RADEONResetI2CTiming(pScrn);

    RADEONResetI2C(pScrn);

}


