linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / media / video / cx25840 / cx25840-firmware.c
index 1958d40..e1a7823 100644 (file)
 #include <linux/i2c-algo-bit.h>
 #include <linux/firmware.h>
 #include <media/v4l2-common.h>
-#include <media/cx25840.h>
 
-#include "cx25840-core.h"
+#include "cx25840.h"
 
 #define FWFILE "v4l-cx25840.fw"
-
-/*
- * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
- * size of the firmware chunks sent down the I2C bus to the chip.
- * Previously this had been set to 1024 but unfortunately some I2C
- * implementations can't transfer data in such big gulps.
- * Specifically, the pvrusb2 driver has a hard limit of around 60
- * bytes, due to the encapsulation there of I2C traffic into USB
- * messages.  So we have to significantly reduce this parameter.
- */
-#define FWSEND 48
+#define FWSEND 1024
 
 #define FWDEV(x) &((x)->adapter->dev)
 
+static int fastfw = 1;
 static char *firmware = FWFILE;
 
+module_param(fastfw, bool, 0444);
 module_param(firmware, charp, 0444);
 
+MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]");
 MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]");
 
+static void set_i2c_delay(struct i2c_client *client, int delay)
+{
+       struct i2c_algo_bit_data *algod = client->adapter->algo_data;
+
+       /* We aren't guaranteed to be using algo_bit,
+        * so avoid the null pointer dereference
+        * and disable the 'fast firmware load' */
+       if (algod) {
+               algod->udelay = delay;
+       } else {
+               fastfw = 0;
+       }
+}
+
 static void start_fw_load(struct i2c_client *client)
 {
        /* DL_ADDR_LB=0 DL_ADDR_HB=0 */
@@ -54,10 +60,16 @@ static void start_fw_load(struct i2c_client *client)
        cx25840_write(client, 0x803, 0x0b);
        /* AUTO_INC_DIS=1 */
        cx25840_write(client, 0x000, 0x20);
+
+       if (fastfw)
+               set_i2c_delay(client, 3);
 }
 
 static void end_fw_load(struct i2c_client *client)
 {
+       if (fastfw)
+               set_i2c_delay(client, 10);
+
        /* AUTO_INC_DIS=0 */
        cx25840_write(client, 0x000, 0x00);
        /* DL_ENABLE=0 */
@@ -84,8 +96,30 @@ static int fw_write(struct i2c_client *client, u8 * data, int size)
        int sent;
 
        if ((sent = i2c_master_send(client, data, size)) < size) {
-               v4l_err(client, "firmware load i2c failure\n");
-               return -ENOSYS;
+
+               if (fastfw) {
+                       v4l_err(client, "333MHz i2c firmware load failed\n");
+                       fastfw = 0;
+                       set_i2c_delay(client, 10);
+
+                       if (sent > 2) {
+                               u16 dl_addr = cx25840_read(client, 0x801) << 8;
+                               dl_addr |= cx25840_read(client, 0x800);
+                               dl_addr -= sent - 2;
+                               cx25840_write(client, 0x801, dl_addr >> 8);
+                               cx25840_write(client, 0x800, dl_addr & 0xff);
+                       }
+
+                       if (i2c_master_send(client, data, size) < size) {
+                               v4l_err(client, "100MHz i2c firmware load failed\n");
+                               return -ENOSYS;
+                       }
+
+               } else {
+                       v4l_err(client, "firmware load i2c failure\n");
+                       return -ENOSYS;
+               }
+
        }
 
        return 0;