summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Pascoe <c.pascoe@itee.uq.edu.au>2007-11-20 00:35:45 +1000
committerChris Pascoe <c.pascoe@itee.uq.edu.au>2007-11-20 00:35:45 +1000
commit2c98c111d17c248cec0002a32688fa71f28c4bc7 (patch)
tree7bd8450a248c23cab7f11543832f7b1b71e56a01
parent5d2f39f6be5040635ca061b69b2dc75b27ab8c4b (diff)
downloadmediapointer-dvb-s2-2c98c111d17c248cec0002a32688fa71f28c4bc7.tar.gz
mediapointer-dvb-s2-2c98c111d17c248cec0002a32688fa71f28c4bc7.tar.bz2
xc2028: retry firmware load if tuner does not respond
From: Chris Pascoe <c.pascoe@itee.uq.edu.au> In practice, the tuner occasionally fails to respond correctly after a firmware load. Retry the firmware load if the firmware/hardware version we read back from the tuner after programming does not match what we expect. Signed-off-by: Chris Pascoe <c.pascoe@itee.uq.edu.au> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--linux/drivers/media/video/tuner-xc2028.c37
1 files changed, 34 insertions, 3 deletions
diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/video/tuner-xc2028.c
index 2737417fe..71cc565fb 100644
--- a/linux/drivers/media/video/tuner-xc2028.c
+++ b/linux/drivers/media/video/tuner-xc2028.c
@@ -86,6 +86,9 @@ struct xc2028_data {
int firm_size;
__u16 firm_version;
+ __u16 hwmodel;
+ __u16 hwvers;
+
struct xc2028_ctrl ctrl;
struct firmware_properties cur_fw;
@@ -622,7 +625,7 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
v4l2_std_id std, fe_bandwidth_t bandwidth)
{
struct xc2028_data *priv = fe->tuner_priv;
- int rc = 0;
+ int rc = 0, is_retry = 0;
unsigned int type = 0;
struct firmware_properties new_fw;
u16 version, hwmodel;
@@ -669,6 +672,7 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
};
}
+retry:
new_fw.type = type;
new_fw.id = std;
new_fw.std_req = std;
@@ -754,14 +758,34 @@ skip_std_specific:
&new_fw.id, new_fw.scode_nr);
check_device:
- xc2028_get_reg(priv, 0x0004, &version);
- xc2028_get_reg(priv, 0x0008, &hwmodel);
+ if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
+ xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
+ tuner_err("Unable to read tuner registers.\n");
+ goto fail;
+ }
tuner_info("Device is Xceive %d version %d.%d, "
"firmware version %d.%d\n",
hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
(version & 0xf0) >> 4, version & 0xf);
+ /* Check firmware version against what we downloaded. */
+ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
+ tuner_err("Incorrect readback of firmware version.\n");
+ goto fail;
+ }
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = version & 0xff00;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != (version & 0xff00)) {
+ tuner_err("Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
/*
@@ -776,6 +800,13 @@ check_device:
fail:
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (!is_retry) {
+ msleep(50);
+ is_retry = 1;
+ tuner_dbg("Retrying firmware load\n");
+ goto retry;
+ }
+
if (rc == -ENOENT)
rc = -EINVAL;
return rc;