dib0700 new i2c implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The attached patch implements the new dib0700 i2c API, which requires
v1.20 of the firmware.  It addresses some classes of i2c problems (in
particular the one I had where i2c reads were being sent onto the bus
as i2c write calls)

I would appreciate it if those with dib0700 based devices would try
out the patch and provide feedback as to whether they have any
problems.  I've done testing with the Pinnacle PCTV HD Pro USB 801e
stick, but I don't have any other dib0700 based devices.

Thanks to Patrick Boettcher for providing the firmware, sample code,
and peer review of the first version of this patch.

Regards,

Devin

-- 
Devin J. Heitmueller
http://www.devinheitmueller.com
AIM: devinheitmueller
Add support for new i2c API provided in firmware version 1.20

From: Devin Heitmueller <devin.heitmueller@xxxxxxxxx>

The Pinnacle PCTV HD Pro has an xc5000, which exposed a bug in the dib0700's
i2c implementation where it did not properly support a single i2c read request
(sending it as an i2c write request instead).  Version 1.20 of the firmware 
added support for a new i2c API which supported such requests.

This change defaults to fw 1.20 for all devices, as well as defaulting to
using the new i2c API.  For testing purposes, the maintainer can disable
use of the new i2c API (falling back to the legacy calls) by putting the
following into their frontend initialization:

struct dib0700_state *st = adap->dev->priv;
st->fw_use_legacy_i2c_api = 1;

Also note that the code expects i2c repeated start to be supported.  If the
i2c slave does not support repeated start, i2c messsages should have the
I2C_M_NOSTART flag set.

Thanks to Patrick Boettcher <patrick.boettcher@xxxxxxx> for providing new
firmware fixing the issue as well as example i2c code utilizing the interface.

Signed-off-by: Devin Heitmueller <devin.heitmueller@xxxxxxxxx>

diff -r a4843e1304e6 linux/drivers/media/dvb/dvb-usb/dib0700.h
--- a/linux/drivers/media/dvb/dvb-usb/dib0700.h	Sun Aug 24 12:28:11 2008 -0300
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700.h	Tue Aug 26 21:02:49 2008 -0400
@@ -31,6 +31,8 @@ extern int dvb_usb_dib0700_debug;
 	// 2 Byte: MPEG2 mode:  4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
 	// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines)    4LSB(     "                "           )
 #define REQUEST_SET_RC       0x11
+#define REQUEST_NEW_I2C_READ 0x12
+#define REQUEST_NEW_I2C_WRITE 0x13
 #define REQUEST_GET_VERSION  0x15
 
 struct dib0700_state {
@@ -39,6 +41,7 @@ struct dib0700_state {
 	u8 rc_toggle;
 	u8 rc_counter;
 	u8 is_dib7000pc;
+	u8 fw_use_legacy_i2c_api;
 };
 
 extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
diff -r a4843e1304e6 linux/drivers/media/dvb/dvb-usb/dib0700_core.c
--- a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c	Sun Aug 24 12:28:11 2008 -0300
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c	Tue Aug 26 21:02:49 2008 -0400
@@ -82,9 +82,98 @@ int dib0700_set_gpio(struct dvb_usb_devi
 }
 
 /*
- * I2C master xfer function
+ * I2C master xfer function (supported in 1.20 firmware)
  */
-static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+				int num)
+{
+	/* The new i2c firmware messages are more reliable and in particular
+	   properly support i2c read calls not preceded by a write */
+
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	uint8_t bus_mode = 1;  /* 0=eeprom bus, 1=frontend bus */
+	uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+	uint8_t en_start = 0;
+	uint8_t en_stop = 0;
+	uint8_t buf[255]; /* TBV: malloc ? */
+	int result, i;
+
+	/* Ensure nobody else hits the i2c bus while we're sending our
+	   sequence of messages, (such as the remote control thread) */
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	for (i = 0; i < num; i++) {
+		if (i == 0) {
+			/* First message in the transaction */
+			en_start = 1;
+		} else if (!(msg[i].flags & I2C_M_NOSTART)) {
+			/* Device supports repeated-start */
+			en_start = 1;
+		} else {
+			/* Not the first packet and device doesn't support
+			   repeated start */
+			en_start = 0;
+		}
+		if (i == (num - 1)) {
+			/* Last message in the transaction */
+			en_stop = 1;
+		}
+
+		if (msg[i].flags & I2C_M_RD) {
+			/* Read request */
+			u16 index, value;
+			uint8_t i2c_dest;
+
+			i2c_dest = (msg[i].addr << 1);
+			value = ((en_start << 7) | (en_stop << 6) |
+				 (msg[i].len & 0x3F)) << 8 | i2c_dest;
+			/* I2C ctrl + FE bus; */
+			index = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+
+			result = usb_control_msg(d->udev,
+						 usb_rcvctrlpipe(d->udev, 0),
+						 REQUEST_NEW_I2C_READ,
+						 USB_TYPE_VENDOR | USB_DIR_IN,
+						 value, index, msg[i].buf,
+						 msg[i].len,
+						 USB_CTRL_GET_TIMEOUT);
+			if (result < 0) {
+				err("i2c read error (status = %d)\n", result);
+				break;
+			}
+		} else {
+			/* Write request */
+			buf[0] = REQUEST_NEW_I2C_WRITE;
+			buf[1] = (msg[i].addr << 1);
+			buf[2] = (en_start << 7) | (en_stop << 6) |
+				(msg[i].len & 0x3F);
+			/* I2C ctrl + FE bus; */
+			buf[3] = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+			/* The Actual i2c payload */
+			memcpy(&buf[4], msg[i].buf, msg[i].len);
+
+			result = usb_control_msg(d->udev,
+						 usb_sndctrlpipe(d->udev, 0),
+						 REQUEST_NEW_I2C_WRITE,
+						 USB_TYPE_VENDOR | USB_DIR_OUT,
+						 0, 0, buf, msg[i].len + 4,
+						 USB_CTRL_GET_TIMEOUT);
+			if (result < 0) {
+				err("i2c write error (status = %d)\n", result);
+				break;
+			}
+		}
+	}
+	mutex_unlock(&d->i2c_mutex);
+	return i;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+				   struct i2c_msg *msg, int num)
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int i,len;
@@ -122,6 +211,21 @@ static int dib0700_i2c_xfer(struct i2c_a
 
 	mutex_unlock(&d->i2c_mutex);
 	return i;
+}
+
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+			    int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	struct dib0700_state *st = d->priv;
+
+	if (st->fw_use_legacy_i2c_api == 1) {
+		/* Use legacy calls */
+		return dib0700_i2c_xfer_legacy(adap, msg, num);
+	} else {
+		/* User running at least fw 1.20 */
+		return dib0700_i2c_xfer_new(adap, msg, num);
+	}
 }
 
 static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
diff -r a4843e1304e6 linux/drivers/media/dvb/dvb-usb/dib0700_devices.c
--- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c	Sun Aug 24 12:28:11 2008 -0300
+++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c	Tue Aug 26 21:02:49 2008 -0400
@@ -1125,7 +1125,7 @@ MODULE_DEVICE_TABLE(usb, dib0700_usb_id_
 #define DIB0700_DEFAULT_DEVICE_PROPERTIES \
 	.caps              = DVB_USB_IS_AN_I2C_ADAPTER, \
 	.usb_ctrl          = DEVICE_SPECIFIC, \
-	.firmware          = "dvb-usb-dib0700-1.10.fw", \
+	.firmware          = "dvb-usb-dib0700-1.20.fw", \
 	.download_firmware = dib0700_download_firmware, \
 	.no_reconnect      = 1, \
 	.size_of_priv      = sizeof(struct dib0700_state), \
_______________________________________________
linux-dvb mailing list
linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux