diff options
author | Chris Pascoe <c.pascoe@itee.uq.edu.au> | 2007-11-19 09:35:45 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:02:31 -0500 |
commit | 8bf799a6217f6336fb95f37bf1b130003404bd7b (patch) | |
tree | 6a184fca900c1c63aa65976d5aac0237536e5e67 /drivers/media/video/tuner-xc2028.c | |
parent | e0f0b37a3e624440b1b0e8a5978b367895226e75 (diff) |
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>
Diffstat (limited to 'drivers/media/video/tuner-xc2028.c')
-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 { | |||
75 | int firm_size; | 75 | int firm_size; |
76 | __u16 firm_version; | 76 | __u16 firm_version; |
77 | 77 | ||
78 | __u16 hwmodel; | ||
79 | __u16 hwvers; | ||
80 | |||
78 | struct xc2028_ctrl ctrl; | 81 | struct xc2028_ctrl ctrl; |
79 | 82 | ||
80 | struct firmware_properties cur_fw; | 83 | struct firmware_properties cur_fw; |
@@ -607,7 +610,7 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, | |||
607 | v4l2_std_id std, fe_bandwidth_t bandwidth) | 610 | v4l2_std_id std, fe_bandwidth_t bandwidth) |
608 | { | 611 | { |
609 | struct xc2028_data *priv = fe->tuner_priv; | 612 | struct xc2028_data *priv = fe->tuner_priv; |
610 | int rc = 0; | 613 | int rc = 0, is_retry = 0; |
611 | unsigned int type = 0; | 614 | unsigned int type = 0; |
612 | struct firmware_properties new_fw; | 615 | struct firmware_properties new_fw; |
613 | u16 version, hwmodel; | 616 | u16 version, hwmodel; |
@@ -654,6 +657,7 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, | |||
654 | }; | 657 | }; |
655 | } | 658 | } |
656 | 659 | ||
660 | retry: | ||
657 | new_fw.type = type; | 661 | new_fw.type = type; |
658 | new_fw.id = std; | 662 | new_fw.id = std; |
659 | new_fw.std_req = std; | 663 | new_fw.std_req = std; |
@@ -739,14 +743,34 @@ skip_std_specific: | |||
739 | &new_fw.id, new_fw.scode_nr); | 743 | &new_fw.id, new_fw.scode_nr); |
740 | 744 | ||
741 | check_device: | 745 | check_device: |
742 | xc2028_get_reg(priv, 0x0004, &version); | 746 | if (xc2028_get_reg(priv, 0x0004, &version) < 0 || |
743 | xc2028_get_reg(priv, 0x0008, &hwmodel); | 747 | xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) { |
748 | tuner_err("Unable to read tuner registers.\n"); | ||
749 | goto fail; | ||
750 | } | ||
744 | 751 | ||
745 | tuner_info("Device is Xceive %d version %d.%d, " | 752 | tuner_info("Device is Xceive %d version %d.%d, " |
746 | "firmware version %d.%d\n", | 753 | "firmware version %d.%d\n", |
747 | hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, | 754 | hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, |
748 | (version & 0xf0) >> 4, version & 0xf); | 755 | (version & 0xf0) >> 4, version & 0xf); |
749 | 756 | ||
757 | /* Check firmware version against what we downloaded. */ | ||
758 | if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { | ||
759 | tuner_err("Incorrect readback of firmware version.\n"); | ||
760 | goto fail; | ||
761 | } | ||
762 | |||
763 | /* Check that the tuner hardware model remains consistent over time. */ | ||
764 | if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) { | ||
765 | priv->hwmodel = hwmodel; | ||
766 | priv->hwvers = version & 0xff00; | ||
767 | } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || | ||
768 | priv->hwvers != (version & 0xff00)) { | ||
769 | tuner_err("Read invalid device hardware information - tuner " | ||
770 | "hung?\n"); | ||
771 | goto fail; | ||
772 | } | ||
773 | |||
750 | memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); | 774 | memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); |
751 | 775 | ||
752 | /* | 776 | /* |
@@ -761,6 +785,13 @@ check_device: | |||
761 | 785 | ||
762 | fail: | 786 | fail: |
763 | memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); | 787 | memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); |
788 | if (!is_retry) { | ||
789 | msleep(50); | ||
790 | is_retry = 1; | ||
791 | tuner_dbg("Retrying firmware load\n"); | ||
792 | goto retry; | ||
793 | } | ||
794 | |||
764 | if (rc == -ENOENT) | 795 | if (rc == -ENOENT) |
765 | rc = -EINVAL; | 796 | rc = -EINVAL; |
766 | return rc; | 797 | return rc; |