Index: ivtv-i2c.c
===================================================================
--- ivtv-i2c.c	(revision 2736)
+++ ivtv-i2c.c	(working copy)
@@ -111,11 +111,304 @@
 	return 0;
 }
 
-static struct i2c_adapter ivtv_i2c_adapter_template = {
+static void setscl(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	writel(~state, itv->reg_mem + IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void setsda(struct ivtv *itv, int state)
+{
+	/* write them out */
+	/* write bits are inverted */
+	writel(~state & 1, itv->reg_mem + IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int getscl(struct ivtv *itv)
+{
+	return readl(itv->reg_mem + IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int getsda(struct ivtv *itv)
+{
+	return readl(itv->reg_mem + IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+static void scldelay(struct ivtv *itv)
+{
+	int i;
+	for (i = 0; i < 5; ++i)
+		(void) getscl(itv);
+}
+
+static int waitscl(struct ivtv *itv, int val)
+{
+	int i;
+	
+	scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (getscl(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+static int waitsda(struct ivtv *itv, int val)
+{
+	int i;
+	
+	scldelay(itv);
+	for (i = 0; i < 1000; ++i) {
+		if (getsda(itv) == val)
+			return 1;
+	}
+	return 0;
+}
+
+static int ack(struct ivtv *itv)
+{
+	int ret = 0;
+	
+	if (getscl(itv) == 1) {
+		IVTV_DEBUG_I2C("SCL was high starting an ack\n");
+		setscl(itv, 0);
+		if (!waitscl(itv, 0)) {
+			/* TODO: */
+			IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+			return -EREMOTEIO;
+		}
+	}
+	setsda(itv, 1);
+	scldelay(itv);
+	setscl(itv, 1);
+	if (!waitsda(itv, 0)) {
+		IVTV_DEBUG_I2C("Slave did not ack\n");
+		ret = -EREMOTEIO;
+	}
+	setscl(itv, 0);
+	if (!waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+static int sendbyte(struct ivtv *itv, unsigned char byte)
+{
+	int i, bit;
+	
+	IVTV_DEBUG_I2C("write %x\n",byte);
+	for (i = 0; i < 8; ++i, byte<<=1) {
+		setscl(itv, 0);
+		if (!waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("Error setting SCL low\n");
+			return -EREMOTEIO;
+		}
+		bit = (byte>>7)&1;
+		setsda(itv, bit);
+		if (!waitsda(itv, bit)) {
+			IVTV_DEBUG_I2C("Error setting SDA\n");
+			return -EREMOTEIO;
+		}
+		setscl(itv, 1);
+		if (!waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Slave not ready for bit\n");
+			return -EREMOTEIO;
+		}
+	}
+	setscl(itv, 0);
+	if (!waitscl(itv, 0)) {
+		IVTV_DEBUG_I2C("Error setting SCL low\n");
+		return -EREMOTEIO;
+	}
+	return ack(itv);
+}
+
+static int readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+	int i;
+	
+	*byte = 0;
+	
+	setsda(itv, 1);
+	scldelay(itv);
+	for (i = 0; i < 8; ++i) {
+		setscl(itv, 0);
+		scldelay(itv);
+		setscl(itv, 1);
+		if (!waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("Error setting SCL high\n");
+			return -EREMOTEIO;
+		}
+		*byte = ((*byte)<<1)|getsda(itv);
+	}
+	setscl(itv, 0);
+	scldelay(itv);
+	setsda(itv, nack);
+	scldelay(itv);
+	setscl(itv, 1);
+	scldelay(itv);
+	setscl(itv, 0);
+	scldelay(itv);
+	IVTV_DEBUG_I2C("read %x\n",*byte);
+	return 0;
+}
+
+static int start(struct ivtv *itv)
+{
+	int sda; 
+	
+	sda = getsda(itv);
+	if (sda != 1) {
+		IVTV_DEBUG_I2C("SDA was low at start\n");
+		setsda(itv, 1);
+		if (!waitsda(itv, 1)) {
+			IVTV_DEBUG_I2C("SDA stuck low\n");
+			return -EREMOTEIO;
+		}
+	}
+	if (getscl(itv) != 1) {
+		setscl(itv, 1);
+		if (!waitscl(itv, 1)) {
+			IVTV_DEBUG_I2C("SCL stuck low at start\n");
+			return -EREMOTEIO;
+		}
+	}
+	setsda(itv, 0);
+	scldelay(itv);
+	return 0;
+}
+
+
+static int stop(struct ivtv *itv)
+{
+	int i;
+	
+	if (getscl(itv) != 0) {
+		IVTV_DEBUG_I2C("SCL not low when stopping\n");
+		setscl(itv, 0);
+		if (!waitscl(itv, 0)) {
+			IVTV_DEBUG_I2C("SCL could not be set low\n");
+		}
+	}
+	setsda(itv, 0);
+	scldelay(itv);
+	setscl(itv, 1);
+	if (!waitscl(itv, 1)) {
+		IVTV_DEBUG_I2C("SCL could not be set high\n");
+		return -EREMOTEIO;
+	}
+	scldelay(itv);
+	setsda(itv, 1);
+	if (!waitsda(itv, 1)) {
+		IVTV_DEBUG_I2C("resetting I2C\n");
+		for (i = 0; i < 16; ++i) {
+			setscl(itv, 0);
+			scldelay(itv);
+			setscl(itv, 1);
+			scldelay(itv);
+			setsda(itv, 1);
+		}
+		waitsda(itv, 1);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+	
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = start(itv);
+		
+		if (ret == 0) {
+			ret = sendbyte(itv, addr<<1);
+			for (i = 0; ret == 0 && i < len; ++i)
+				ret = sendbyte(itv, data[i]);
+		}
+		if (ret != 0 || do_stop) {
+			(void) stop(itv);
+		}
+	}
+	if (ret == 0) {
+		IVTV_DEBUG_I2C("i2c write to %x succeeded\n", addr);
+	} else {
+		IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+	}
+	return ret;
+}
+
+static int read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+	int retry, ret = -EREMOTEIO;
+	u32 i;
+	
+	for (retry = 0; ret != 0 && retry < 8; ++retry) {
+		ret = start(itv);
+		if (ret == 0)
+			ret = sendbyte(itv, (addr << 1) | 1);
+		for (i = 0; ret == 0 && i < len; ++i) {
+			ret = readbyte(itv, &data[i], i == len - 1);
+		}
+		(void) stop(itv);
+	}
+	if (ret == 0) {
+		IVTV_DEBUG_I2C("i2c read from %x succeeded\n", addr);
+	} else {
+		IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+	}
+	return ret;
+}
+
+static int xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+	struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+	int retval = 0;
+	int i;
+
+	down(&itv->i2c_bus_lock);
+	for (i = 0 ; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD) {
+			IVTV_DEBUG_I2C("xfer: read %d/%d\n", i, num);
+			/* read */
+			retval = read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+			if (retval < 0)
+				goto err;
+		} else {
+			IVTV_DEBUG_I2C("xfer: write %d/%d\n", i, num);
+			/* write */
+			retval = write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, 1);
+			if (retval < 0)
+				goto err;
+		}
+	}
+	up(&itv->i2c_bus_lock);
+	return num;
+
+err:
+ 	up(&itv->i2c_bus_lock);
+	return retval;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+	.master_xfer   = xfer,
+	.functionality = functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
 	.name = "ivtv i2c driver",
-	.id = I2C_HW_B_BT848,	/* algo-bit is OR'd with this */
-	.algo = NULL,		/* set by i2c-algo-bit */
-	.algo_data = NULL,	/* filled from template */
+	.id = I2C_ALGO_BIT|I2C_HW_B_BT848,	/* algo-bit is OR'd with this */
+	.algo = &ivtv_algo,
+	.algo_data = NULL,			/* filled from template */
 	.client_register = attach_inform,
 	.client_unregister = detach_inform,
 #ifndef NEW_I2C
@@ -131,6 +424,7 @@
 #endif /* LINUX26 */
 };
 
+
 static void ivtv_setscl(void *data, int state)
 {
 	struct ivtv *itv = (struct ivtv *)data;
@@ -173,6 +467,27 @@
 	return readb(itv->reg_mem + IVTV_REG_I2C_GETSDA_OFFSET);
 }
 
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+	.name = "ivtv i2c driver",
+	.id = I2C_HW_B_BT848,			/* algo-bit is OR'd with this */
+	.algo = NULL,				/* set by i2c-algo-bit */
+	.algo_data = NULL,			/* filled from template */
+	.client_register = attach_inform,
+	.client_unregister = detach_inform,
+#ifndef NEW_I2C
+/* pre i2c-2.8.0 */
+	.inc_use = ivtv_i2c_inc,	/* inc usage */
+	.dec_use = ivtv_i2c_dec,	/* dec usage */
+#else
+/* i2c-2.8.0 and later */
+	.owner = THIS_MODULE,
+#endif
+#ifdef LINUX26
+	.class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
 static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
 	NULL,			/* ?? */
 	ivtv_setsda,		/* setsda function */
@@ -188,6 +503,8 @@
 	.name = "ivtv internal use only",
 };
 
+
+
 static int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd,
 				void *arg)
 {
@@ -285,30 +602,44 @@
 int __devinit init_ivtv_i2c(struct ivtv *itv)
 {
 	IVTV_DEBUG_I2C("i2c init\n");
-	memcpy(&itv->i2c_adap, &ivtv_i2c_adapter_template,
-	       sizeof(struct i2c_adapter));
-	memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
-	       sizeof(struct i2c_algo_bit_data));
-	memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
-	       sizeof(struct i2c_client));
+ 
+ 	if (itv->options.newi2c) {
+ 		memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
+ 		       sizeof(struct i2c_adapter));
+ 	} else {
+ 		memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+ 		       sizeof(struct i2c_adapter));
+ 		memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+ 		       sizeof(struct i2c_algo_bit_data));
+ 		itv->i2c_algo.data = itv;
+		itv->i2c_adap.algo_data = &itv->i2c_algo;
+	}
 
 	sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
 		itv->num);
-	itv->i2c_algo.data = itv;
 	i2c_set_adapdata(&itv->i2c_adap, itv);
-	itv->i2c_adap.algo_data = &itv->i2c_algo;
+
+	memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+	       sizeof(struct i2c_client));
 	itv->i2c_client.adapter = &itv->i2c_adap;
-
+  
 	IVTV_DEBUG_I2C("setting scl and sda to 1\n");
-	ivtv_setscl(itv, 1);
-	ivtv_setsda(itv, 1);
+	setscl(itv, 1);
+	setsda(itv, 1);
 
-	return i2c_bit_add_bus(&itv->i2c_adap);
+	if (itv->options.newi2c)
+		return i2c_add_adapter(&itv->i2c_adap);
+	else
+		return i2c_bit_add_bus(&itv->i2c_adap);
 }
 
 void __devexit exit_ivtv_i2c(struct ivtv *itv)
 {
 	IVTV_DEBUG_I2C("i2c exit\n");
 
-	i2c_bit_del_bus(&itv->i2c_adap);
+	if (itv->options.newi2c) {
+		i2c_del_adapter(&itv->i2c_adap);
+	} else {
+		i2c_bit_del_bus(&itv->i2c_adap);
+	}
 }
Index: ivtv-driver.c
===================================================================
--- ivtv-driver.c	(revision 2736)
+++ ivtv-driver.c	(working copy)
@@ -53,6 +53,7 @@
 #include "ivtv-audio.h"
 #include "ivtv-dma.h"
 #include "cx25840.h"
+#include "ivtv-gpio.h"
 
 #ifdef LINUX26
 #include <linux/vermagic.h>
@@ -167,6 +168,8 @@
 
 int errno;
 
+int newi2c = 1;
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
 module_param(yuv_fixup, int, 0644);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
@@ -207,6 +210,7 @@
 module_param(max_dec_yuv_buffers, int, 0644);
 module_param(max_dec_vbi_buffers, int, 0644);
 module_param(max_dec_osd_buffers, int, 0644);
+module_param(newi2c, int, 0644);
 #else
 MODULE_PARM(yuv_fixup, "i");
 MODULE_PARM(tuner, "1-" __stringify(IVTV_MAX_CARDS) "i");
@@ -240,7 +244,7 @@
 MODULE_PARM(max_dec_yuv_buffers, "i");
 MODULE_PARM(max_dec_vbi_buffers, "i");
 MODULE_PARM(max_dec_osd_buffers, "i");
-
+MODULE_PARM(newi2c, "i")
 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
 
 MODULE_PARM_DESC(yuv_fixup,
@@ -342,6 +346,10 @@
 		 "Max Dec OSD Buffers (in megs)\n"
 		 "\t\t\tDefault: " IVTV_MAX_DEC_OSD_BUFFERS_CNT);
 
+MODULE_PARM_DESC(newi2c,
+		 "Use new I2C implementation\n"
+		 "\t\t\t default is 1 (yes)");
+
 MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
 
 MODULE_AUTHOR("Kevin Thayer");
@@ -569,6 +577,7 @@
 	itv->options.radio = radio[itv->num];
 	itv->options.tda9887 = tda9887[itv->num];
 	itv->options.dynbuf = ivtv_dynbuf;
+	itv->options.newi2c = newi2c;
 
         itv->has_itvc15 = (itv->dev->device == PCI_DEVICE_ID_IVTV15);
         chipname = itv->has_itvc15 ? "iTVC15" : "iTVC16";
@@ -653,6 +662,7 @@
 	init_MUTEX(&itv->i2c_lock);
 	init_MUTEX(&itv->DMA_lock);
 	init_MUTEX(&itv->vbi_dec_lock);
+	init_MUTEX(&itv->i2c_bus_lock);
 
 	itv->DMA_slock = SPIN_LOCK_UNLOCKED;
 
@@ -1504,6 +1514,7 @@
 EXPORT_SYMBOL(ivtv_vapi);
 EXPORT_SYMBOL(ivtv_clear_irq_mask);
 EXPORT_SYMBOL(ivtv_debug);
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
 #endif /* LINUX26 */
 module_init(module_start);
 module_exit(module_cleanup);
Index: ivtv-driver.h
===================================================================
--- ivtv-driver.h	(revision 2736)
+++ ivtv-driver.h	(working copy)
@@ -629,6 +629,7 @@
 	int radio;		/* enable/disable radio */
         int tda9887;
 	int dynbuf;		/* Enable/Disable dynamic buffers */
+	int newi2c;		/* New I2C algorithm */
 };
 
 struct ivtv_dec_options {
@@ -1055,6 +1056,7 @@
 	struct i2c_adapter i2c_adap;
 	struct i2c_algo_bit_data i2c_algo;
 	struct i2c_client i2c_client;
+	struct semaphore i2c_bus_lock;
 	int i2c_state;
 	struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
 
Index: cx25840-firmware.c
===================================================================
--- cx25840-firmware.c	(revision 2736)
+++ cx25840-firmware.c	(working copy)
@@ -31,7 +31,8 @@
 static inline void cx25840_i2c_set_delay(struct i2c_client *client, int delay)
 {
 	struct i2c_algo_bit_data *algod = client->adapter->algo_data;
-	algod->udelay = delay;
+	if (algod)
+		algod->udelay = delay;
 }
 
 static inline void start_fw_load(struct i2c_client *client)
