#include "saa7146.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
	#define KBUILD_MODNAME "saa7146"
#endif

/* helper function */
void my_wait(struct saa7146_dev *dev, long ms)
{
	unsigned long timeout = jiffies + ((ms+9)/10);
	while(time_before(jiffies, timeout)) {
		schedule();
	}
}

u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
//fm	DEB_I2C(("'%s'.\n", adapter->name));

	return	  I2C_FUNC_I2C
		| I2C_FUNC_SMBUS_QUICK
		| I2C_FUNC_SMBUS_READ_BYTE	| I2C_FUNC_SMBUS_WRITE_BYTE
		| I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
}

/* this function returns the status-register of our i2c-device */
u32 saa7146_i2c_status(struct saa7146_dev *dev) 
{
	u32 iicsta = saa7146_read(dev, I2C_STATUS);
/*
	DEB_I2C(("status: 0x%08x\n",iicsta));
*/
	return iicsta;
}

/* this function runs through the i2c-messages and prepares the data to be
   sent through the saa7146. have a look at the specifications p. 122 ff 
   to understand this. it returns the number of u32s to send, or -1
   in case of an error. */
int saa7146_i2c_msg_prepare(const struct i2c_msg m[], int num, u32 *op)
{
	int h1, h2;
	int i, j, addr;
	int mem = 0, op_count = 0;

	/* first determine size of needed memory */
	for(i = 0; i < num; i++) {
		mem += m[i].len + 1;
	}

	/* worst case: we need one u32 for three bytes to be send
	   plus one extra byte to address the device */
	mem = 1 + ((mem-1) / 3);

	/* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
	   size. if we exceed this limit... */
	if ( (4*mem) > SAA7146_I2C_MEM ) {
//fm		DEB_I2C(("cannot prepare i2c-message.\n"));
		return -1;
	}

	/* be careful: clear out the i2c-mem first */
	memset(op,0,sizeof(u32)*mem);

	/* loop through all messages */
	for(i = 0; i < num; i++) {

		/* insert the address of the i2c-slave.
		   note: we get 7 bit i2c-addresses, so we have to perform a translation */
		addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0); 
		h1 = op_count/3; h2 = op_count%3;
		op[h1] |= (	    (u8)addr << ((3-h2)*8));
		op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
		op_count++;

		/* loop through all bytes of message i */
		for(j = 0; j < m[i].len; j++) {
		
			/* insert the data bytes */
			h1 = op_count/3; h2 = op_count%3;
			op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
			op[h1] |= (       SAA7146_I2C_CONT << ((3-h2)*2));
			op_count++;
		}
		
	}			

	/* have a look at the last byte inserted:
	  if it was: ...CONT change it to ...STOP */
	h1 = (op_count-1)/3; h2 = (op_count-1)%3;
	if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
		op[h1] &= ~(0x2 << ((3-h2)*2));
		op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
	}

	/* return the number of u32s to send */	
	return mem;
}

/* this functions loops through all i2c-messages. normally, it should determine
   which bytes were read through the adapter and write them back to the corresponding
   i2c-message. but instead, we simply write back all bytes.
   fixme: this could be improved. */
int saa7146_i2c_msg_cleanup(const struct i2c_msg m[], int num, u32 *op)
{
	int i, j;
	int op_count = 0;

	/* loop through all messages */
	for(i = 0; i < num; i++) {

		op_count++;

		/* loop throgh all bytes of message i */
		for(j = 0; j < m[i].len; j++) {
			/* write back all bytes that could have been read */
			m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
			op_count++;
		}
	}
	
	return 0;
}

/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
int saa7146_i2c_reset(struct saa7146_dev *dev) 
{
	/* get current status */
	u32 status = saa7146_i2c_status(dev);
	
	/* clear registers for sure */
	saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
	saa7146_write(dev, I2C_TRANSFER, 0);

	/* check if any operation is still in progress */
	if ( 0 != ( status & SAA7146_I2C_BUSY) ) {

		/* yes, kill ongoing operation */
		DEB_I2C(("busy_state detected.\n"));

		/* set "ABORT-OPERATION"-bit (bit 7)*/
		saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
		my_wait(dev,SAA7146_I2C_DELAY);

		/* clear all error-bits pending; this is needed because p.123, note 1 */
		saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
		my_wait(dev,SAA7146_I2C_DELAY);
 	}

	/* check if any error is (still) present. (this can be necessary because p.123, note 1) */
	status = saa7146_i2c_status(dev);

	if ( dev->i2c_bitrate != status ) {

		DEB_I2C(("error_state detected. status:0x%08x\n",status));

		/* Repeat the abort operation. This seems to be necessary
		   after serious protocol errors caused by e.g. the SAA7740 */
		saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
		my_wait(dev,SAA7146_I2C_DELAY);

		/* clear all error-bits pending */
		saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
		my_wait(dev,SAA7146_I2C_DELAY);

		/* the data sheet says it might be necessary to clear the status
		   twice after an abort */
		saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
		my_wait(dev,SAA7146_I2C_DELAY);
     	}

	/* if any error is still present, a fatal error has occured ... */
	status = saa7146_i2c_status(dev);
	if ( dev->i2c_bitrate != status ) {
		DEB_I2C(("fatal error. status:0x%08x\n",status));
		return -1;
	}

	return 0;
}

/* this functions writes out the data-byte 'dword' to the i2c-device.
   it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
   failed badly (e.g. address error) */
int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword)
{
	int i = 0;
	u32 status = 0, mc2 = 0;

	DEB_I2C(("before: 0x%08x\n",*dword));
	
	/* write out i2c-command */
	saa7146_write(dev, I2C_STATUS,	 dev->i2c_bitrate);
	saa7146_write(dev, I2C_TRANSFER, *dword);
	saa7146_write(dev, MC2, (MASK_00 | MASK_16));

	/* do not poll for i2c-status before upload is complete */
	for (i = 5; i > 0; i--) {
		mc2 = (saa7146_read(dev, MC2) & 0x1);
		if( 0 != mc2 )
			break;
		my_wait(dev,SAA7146_I2C_DELAY);
	}
	if (0 == i) {
		DEB_I2C(("timeout error. #1\n"));
		return -1;
	}

	/* wait until busy flag becomes inactive or we time out */
	for (i = 5; i > 0; i--) {
		status = saa7146_i2c_status(dev);
		/* check busy flag */
		if ( 0 == (status & SAA7146_I2C_BUSY))
			break;
		/* check error flag */
		if ( 0 != (status & SAA7146_I2C_ERR)) {
			break;
		}
		my_wait(dev,SAA7146_I2C_DELAY);
	}
	if (0 == i) {
		DEB_I2C(("timeout error. #2\n"));
		return -1;
	}

	/* give a detailed status report */
	if ( 0 != (status & SAA7146_I2C_ERR)) {

		if( 0 != (status & SAA7146_I2C_SPERR) ) {
			DEB_I2C(("error due to invalid start/stop condition.\n"));
		}
		if( 0 != (status & SAA7146_I2C_DTERR) ) {
			DEB_I2C(("error in data transmission.\n"));
		}
		if( 0 != (status & SAA7146_I2C_DRERR) ) {
			DEB_I2C(("error when receiving data.\n"));
		}
		if( 0 != (status & SAA7146_I2C_AL) ) {
			DEB_I2C(("error because arbitration lost.\n"));
		}

		/* we handle address-errors here */
		if( 0 != (status & SAA7146_I2C_APERR) ) {
			DEB_I2C(("error in address phase.\n"));
			return -2;
		}
		return -1;
	}

	/* read back data, just in case we were reading ... */
	*dword = saa7146_read(dev, I2C_TRANSFER);

	DEB_I2C(("after: 0x%08x\n",*dword));
	
	saa7146_i2c_status(dev);

	return 0;
}

int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg msgs[], int num,  int retries)
{
	int i = 0, result = 0, count = 0;
	u32* buffer = dev->i2c_mem;
	int err = 0;
	
	if (down_interruptible (&dev->i2c_lock))
		return -ERESTARTSYS;

	for(i=0;i<num;i++) {
		DEB_I2C(("msg:%d/%d\n",i+1,num));
	}
	
	/* prepare the message(s), get number of u32s to transfer */
	count = saa7146_i2c_msg_prepare(msgs, num, buffer);
	if ( 0 > count ) {
		err = -1;
		goto out;
	}
	
	/* loop through number of retries ... */
	for(; retries >= 0; retries--) {
	
		/* reset the i2c-device if necessary */
		result = saa7146_i2c_reset(dev);
		if ( 0 > result ) {
			DEB_I2C(("could not reset i2c-device.\n"));
			err = -1;
			goto out;
		}	

		/* write out the u32s one after another */
		for(i = 0; i < count; i++) {

			result = saa7146_i2c_writeout(dev, &buffer[i] );
			if ( 0 != result) {
				/* if address-error occured, don�t retry */
				if ( -2 == result ) {
					err = -1;
					goto out;
				}
				DEB_I2C(("error while sending message(s). starting again.\n"));
				break;
			}
		}
		
		/* see if an error occured & the last retry failed */
		if( (0 != result) && (0 == retries) ) {
			DEB_I2C(("could not transfer i2c-message(s).\n"));
			err = -1;
			goto out;
		}
		
		if( 0 == result )
			break;
	}
	
	/* if any things had to be read, get the results */
	if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
		DEB_I2C(("could not cleanup i2c-message.\n"));
		err = -1;
		goto out;
	}

	saa7146_i2c_status(dev);

	/* another bug in revision 0: the i2c-registers get uploaded randomly by other
	   uploads, so we better clear them out before continueing */	
	if( 0 == dev->revision ) {
		u32 zero = 0;
		if( 0 != saa7146_i2c_writeout(dev, &zero)) {
			INFO(("revision 0 error. this should never happen.\n"));
		}
	}

	/* return the number of delivered messages */
	err = num;
	DEB_I2C(("transmission successful. (msg:%d).\n",err));
out:
	up(&dev->i2c_lock);
	return err;	
}

/* utility functions */
int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg msg[], int num)
{
	struct saa7146_dev* dev = (struct saa7146_dev*)adapter->data;
	
	DEB_I2C(("adapter: '%s'.\n", adapter->name));
	
	/* use helper function to transfer data */
	return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
}

/* these are just for completeness */
int saa7146_i2c_reg(struct i2c_client *client)
{
	return 0;
}

int saa7146_i2c_unreg(struct i2c_client *client)
{
	return 0;
}

/* fixme
void saa7146_i2c_inc_use(struct i2c_adapter *adap)
{
#ifdef MODULE
	MOD_INC_USE_COUNT;
#endif
}

void saa7146_i2c_dec_use(struct i2c_adapter *adap)
{
#ifdef MODULE
	MOD_DEC_USE_COUNT;
#endif
}
*/

/*****************************************************************************/
/* i2c-adapter helper functions                                              */

/* exported algorithm data */
struct i2c_algorithm saa7146_algo = {
	"saa7146 i2c algorithm",
	I2C_ALGO_SAA7146,
	saa7146_i2c_xfer,
	NULL,
	NULL,					/* slave_xmit		*/
	NULL,					/* slave_recv		*/
	NULL,					/* ioctl		*/
	saa7146_i2c_func,			/* functionality	*/
};

int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev)
{
	/* fixme: this should be adjusted by the extension */
	dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_480;

	saa7146_i2c_reset(dev);

	memset(dev->i2c_adapter,0,sizeof(struct i2c_adapter));
	strcpy(dev->i2c_adapter->name, dev->name);	

	dev->i2c_adapter->data		= dev;
	dev->i2c_adapter->algo		= &saa7146_algo;
	dev->i2c_adapter->algo_data	= NULL;
	dev->i2c_adapter->id		= I2C_ALGO_SAA7146;
	
/*	fixme
	dev->i2c_adapter->inc_use		= saa7146_i2c_inc_use;
	dev->i2c_adapter->dec_use		= saa7146_i2c_dec_use;
*/
	dev->i2c_adapter->client_register	= saa7146_i2c_reg;
	dev->i2c_adapter->client_unregister	= saa7146_i2c_unreg;

	dev->i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
	dev->i2c_adapter->retries = SAA7146_I2C_RETRIES;
	
	return 0;
}