summaryrefslogtreecommitdiff
path: root/drivers/media/common
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/common')
-rw-r--r--drivers/media/common/tuners/xc4000.c341
1 files changed, 332 insertions, 9 deletions
diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c
index 6d5ab599e83a..19973887312a 100644
--- a/drivers/media/common/tuners/xc4000.c
+++ b/drivers/media/common/tuners/xc4000.c
@@ -36,7 +36,7 @@
#include "tuner-i2c.h"
#include "tuner-xc2028-types.h"
-static int debug;
+static int debug=1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
@@ -52,8 +52,8 @@ static LIST_HEAD(hybrid_tuner_instance_list);
#define dprintk(level, fmt, arg...) if (debug >= level) \
printk(KERN_INFO "%s: " fmt, "xc4000", ## arg)
-#define XC4000_DEFAULT_FIRMWARE "xc4000-01.fw"
-#define XC4000_DEFAULT_FIRMWARE_SIZE 8434
+#define XC4000_DEFAULT_FIRMWARE "xc4000-02.fw"
+#define XC4000_DEFAULT_FIRMWARE_SIZE 18643
/* struct for storing firmware table */
@@ -85,6 +85,10 @@ struct xc4000_priv {
u32 bandwidth;
u8 video_standard;
u8 rf_mode;
+// struct xc2028_ctrl ctrl;
+ struct firmware_properties cur_fw;
+ __u16 hwmodel;
+ __u16 hwvers;
};
/* Misc Defines */
@@ -568,6 +572,73 @@ static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
return XC_RESULT_SUCCESS;
}
+#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
+static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+ if (type & BASE)
+ printk("BASE ");
+ if (type & INIT1)
+ printk("INIT1 ");
+ if (type & F8MHZ)
+ printk("F8MHZ ");
+ if (type & MTS)
+ printk("MTS ");
+ if (type & D2620)
+ printk("D2620 ");
+ if (type & D2633)
+ printk("D2633 ");
+ if (type & DTV6)
+ printk("DTV6 ");
+ if (type & QAM)
+ printk("QAM ");
+ if (type & DTV7)
+ printk("DTV7 ");
+ if (type & DTV78)
+ printk("DTV78 ");
+ if (type & DTV8)
+ printk("DTV8 ");
+ if (type & FM)
+ printk("FM ");
+ if (type & INPUT1)
+ printk("INPUT1 ");
+ if (type & LCD)
+ printk("LCD ");
+ if (type & NOGD)
+ printk("NOGD ");
+ if (type & MONO)
+ printk("MONO ");
+ if (type & ATSC)
+ printk("ATSC ");
+ if (type & IF)
+ printk("IF ");
+ if (type & LG60)
+ printk("LG60 ");
+ if (type & ATI638)
+ printk("ATI638 ");
+ if (type & OREN538)
+ printk("OREN538 ");
+ if (type & OREN36)
+ printk("OREN36 ");
+ if (type & TOYOTA388)
+ printk("TOYOTA388 ");
+ if (type & TOYOTA794)
+ printk("TOYOTA794 ");
+ if (type & DIBCOM52)
+ printk("DIBCOM52 ");
+ if (type & ZARLINK456)
+ printk("ZARLINK456 ");
+ if (type & CHINA)
+ printk("CHINA ");
+ if (type & F6MHZ)
+ printk("F6MHZ ");
+ if (type & INPUT2)
+ printk("INPUT2 ");
+ if (type & SCODE)
+ printk("SCODE ");
+ if (type & HAS_IF)
+ printk("HAS_IF_%d ", int_freq);
+}
+
static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
v4l2_std_id *id)
{
@@ -577,7 +648,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
printk("%s called, want type=", __func__);
if (debug) {
-// dump_firm_type(type);
+ dump_firm_type(type);
printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
}
@@ -653,8 +724,10 @@ found:
ret:
printk("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
if (debug) {
-// dump_firm_type(type);
+ dump_firm_type(type);
printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
+ if (i < 0)
+ dump_stack();
}
return i;
}
@@ -680,7 +753,6 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
p = priv->firm[pos].ptr;
rc = xc_load_i2c_sequence(fe, p);
- printk("load i2c sequence result=%d\n", rc);
return rc;
}
@@ -791,9 +863,10 @@ static int xc4000_fwupload(struct dvb_frontend *fe)
rc = -ENOMEM;
goto err;
}
- printk("Reading firmware type ");
+
if (debug) {
-// dump_firm_type_and_int_freq(type, int_freq);
+ printk("Reading firmware type ");
+ dump_firm_type_and_int_freq(type, int_freq);
printk("(%x), id %llx, size=%d.\n",
type, (unsigned long long)id, size);
}
@@ -832,6 +905,253 @@ done:
return rc;
}
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id, __u16 int_freq, int scode)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+ u8 direct_mode[4];
+ u8 indirect_mode[5];
+
+ dprintk(1, "%s called\n", __func__);
+
+ if (!int_freq) {
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+ } else {
+ for (pos = 0; pos < priv->firm_size; pos++) {
+ if ((priv->firm[pos].int_freq == int_freq) &&
+ (priv->firm[pos].type & HAS_IF))
+ break;
+ }
+ if (pos == priv->firm_size)
+ return -ENOENT;
+ }
+
+ p = priv->firm[pos].ptr;
+
+ if (priv->firm[pos].type & HAS_IF) {
+ if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+ return -EINVAL;
+ p += 12 * scode;
+ } else {
+ /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
+ * has a 2-byte size header in the firmware format. */
+ if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
+ le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
+ return -EINVAL;
+ p += 14 * scode + 2;
+ }
+
+ tuner_info("Loading SCODE for type=");
+ dump_firm_type_and_int_freq(priv->firm[pos].type,
+ priv->firm[pos].int_freq);
+ printk("(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+
+
+ /* Enter direct-mode */
+ memset(direct_mode, 0, sizeof(direct_mode));
+ direct_mode[1] = 0x05;
+ rc = xc_send_i2c_data(priv, direct_mode, sizeof(direct_mode));
+ if (rc < 0)
+ return -EIO;
+
+ rc = xc_send_i2c_data(priv, p, 12);
+ if (rc != XC_RESULT_SUCCESS)
+ return -EIO;
+
+ /* Switch back to indirect-mode */
+ memset(indirect_mode, 0, sizeof(indirect_mode));
+ indirect_mode[4] = 0x88;
+ rc = xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode));
+ if (rc < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id std, __u16 int_freq)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ struct firmware_properties new_fw;
+ int rc = 0, is_retry = 0;
+ u16 version, hwmodel;
+ v4l2_std_id std0;
+ u8 hw_major, hw_minor, fw_major, fw_minor;
+
+ dprintk(1, "%s called\n", __func__);
+
+ if (!priv->firm) {
+ rc = xc4000_fwupload(fe);
+ if (rc < 0)
+ return rc;
+ }
+
+#ifdef DJH_DEBUG
+ if (priv->ctrl.mts && !(type & FM))
+ type |= MTS;
+#endif
+
+retry:
+ new_fw.type = type;
+ new_fw.id = std;
+ new_fw.std_req = std;
+// new_fw.scode_table = SCODE | priv->ctrl.scode_table;
+ new_fw.scode_table = SCODE;
+ new_fw.scode_nr = 0;
+ new_fw.int_freq = int_freq;
+
+ dprintk(1, "checking firmware, user requested type=");
+ if (debug) {
+ dump_firm_type(new_fw.type);
+ printk("(%x), id %016llx, ", new_fw.type,
+ (unsigned long long)new_fw.std_req);
+ if (!int_freq) {
+ printk("scode_tbl ");
+#ifdef DJH_DEBUG
+ dump_firm_type(priv->ctrl.scode_table);
+ printk("(%x), ", priv->ctrl.scode_table);
+#endif
+ } else
+ printk("int_freq %d, ", new_fw.int_freq);
+ printk("scode_nr %d\n", new_fw.scode_nr);
+ }
+
+ /* No need to reload base firmware if it matches */
+ if (((BASE | new_fw.type) & BASE_TYPES) ==
+ (priv->cur_fw.type & BASE_TYPES)) {
+ dprintk(1, "BASE firmware not changed.\n");
+ goto skip_base;
+ }
+
+ /* Updating BASE - forget about all currently loaded firmware */
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+ /* Reset is needed before loading firmware */
+ rc = xc4000_TunerReset(fe);
+ if (rc < 0)
+ goto fail;
+
+ /* BASE firmwares are all std0 */
+ std0 = 0;
+ rc = load_firmware(fe, BASE | new_fw.type, &std0);
+ if (rc < 0) {
+ printk("Error %d while loading base firmware\n", rc);
+ goto fail;
+ }
+
+ /* Load INIT1, if needed */
+ dprintk(1, "Load init1 firmware, if exists\n");
+
+ rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
+ &std0);
+ if (rc < 0 && rc != -ENOENT) {
+ tuner_err("Error %d while loading init1 firmware\n",
+ rc);
+ goto fail;
+ }
+
+skip_base:
+ /*
+ * No need to reload standard specific firmware if base firmware
+ * was not reloaded and requested video standards have not changed.
+ */
+ if (priv->cur_fw.type == (BASE | new_fw.type) &&
+ priv->cur_fw.std_req == std) {
+ dprintk(1, "Std-specific firmware already loaded.\n");
+ goto skip_std_specific;
+ }
+
+ /* Reloading std-specific firmware forces a SCODE update */
+ priv->cur_fw.scode_table = 0;
+
+ rc = load_firmware(fe, new_fw.type, &new_fw.id);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
+
+ if (rc < 0)
+ goto fail;
+
+skip_std_specific:
+ if (priv->cur_fw.scode_table == new_fw.scode_table &&
+ priv->cur_fw.scode_nr == new_fw.scode_nr) {
+ dprintk(1, "SCODE firmware already loaded.\n");
+ goto check_device;
+ }
+
+ if (new_fw.type & FM)
+ goto check_device;
+
+ /* Load SCODE firmware, if exists */
+ dprintk(1, "Trying to load scode %d\n", new_fw.scode_nr);
+
+ rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+ new_fw.int_freq, new_fw.scode_nr);
+
+check_device:
+ rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel);
+
+ if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major,
+ &fw_minor) != XC_RESULT_SUCCESS) {
+ printk("Unable to read tuner registers.\n");
+ goto fail;
+ }
+
+ dprintk(1, "Device is Xceive %d version %d.%d, "
+ "firmware version %d.%d\n",
+ hwmodel, hw_major, hw_minor, fw_major, fw_minor);
+
+ /* Check firmware version against what we downloaded. */
+#ifdef DJH_DEBUG
+ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
+ printk("Incorrect readback of firmware version %x.\n",
+ (version & 0xff));
+ goto fail;
+ }
+#endif
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 && hwmodel == 4000) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = version & 0xff00;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != (version & 0xff00)) {
+ printk("Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
+ memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+ /*
+ * By setting BASE in cur_fw.type only after successfully loading all
+ * firmwares, we can:
+ * 1. Identify that BASE firmware with type=0 has been loaded;
+ * 2. Tell whether BASE firmware was just changed the next time through.
+ */
+ priv->cur_fw.type |= BASE;
+
+ return 0;
+
+fail:
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (!is_retry) {
+ msleep(50);
+ is_retry = 1;
+ dprintk(1, "Retrying firmware load\n");
+ goto retry;
+ }
+
+ if (rc == -ENOENT)
+ rc = -EINVAL;
+ return rc;
+}
static void xc_debug_dump(struct xc4000_priv *priv)
{
@@ -1295,6 +1615,7 @@ struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
/* FIXME: For now, load the firmware at startup. We will remove this
before the code goes to production... */
+#ifdef DJH_DEBUG
xc4000_fwupload(fe);
printk("xc4000_fwupload done\n");
@@ -1308,11 +1629,13 @@ struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
}
/* Load INIT1, if needed */
- tuner_dbg("Load init1 firmware, if exists\n");
+ dprintk("Load init1 firmware, if exists\n");
// rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
rc = load_firmware(fe, BASE | INIT1, &std0);
printk("init1 load result %x\n", rc);
+#endif
+ check_firmware(fe, DTV8, 0, 5400);
if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
goto fail;