#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 */
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 */
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;