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