diff options
author | Chris Pascoe <c.pascoe@itee.uq.edu.au> | 2007-11-19 11:35:45 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 19:02:31 -0200 |
commit | 8bf799a6217f6336fb95f37bf1b130003404bd7b (patch) | |
tree | 6a184fca900c1c63aa65976d5aac0237536e5e67 | |
parent | e0f0b37a3e624440b1b0e8a5978b367895226e75 (diff) | |
download | blackbird-op-linux-8bf799a6217f6336fb95f37bf1b130003404bd7b.tar.gz blackbird-op-linux-8bf799a6217f6336fb95f37bf1b130003404bd7b.zip |
V4L/DVB (6647): xc2028: retry firmware load if tuner does not respond
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-- | drivers/media/video/tuner-xc2028.c | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 8140d8ad0792..cc6fa2fa859b 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c @@ -75,6 +75,9 @@ struct xc2028_data { int firm_size; __u16 firm_version; + __u16 hwmodel; + __u16 hwvers; + struct xc2028_ctrl ctrl; struct firmware_properties cur_fw; @@ -607,7 +610,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; @@ -654,6 +657,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; @@ -739,14 +743,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)); /* @@ -761,6 +785,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; |