aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/tuner-xc2028.c
diff options
context:
space:
mode:
authorChris Pascoe <c.pascoe@itee.uq.edu.au>2007-11-19 09:22:03 -0500
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-25 16:02:30 -0500
commite0f0b37a3e624440b1b0e8a5978b367895226e75 (patch)
tree98135357e75ca922b093996b3e5a15d68978c8f0 /drivers/media/video/tuner-xc2028.c
parent59a636e50f339f91880b3a1e395829c43cc6541a (diff)
V4L/DVB (6646): xc2028: rework firmware (re)loading process
Define a list of valid "firmware types" for each combination of BASE, DTV and SCODEs. By masking the appropriate firmware bits off we can just use one "type" for the firmware searching and also flag when we are looking for a BASE, DTV or SCODE type firmware. This makes it much easier to track if we need to change device modes or flash an individual firmware part. Add a structure to remember what firmware properties we have. This contains the currently loaded/wanted base firmware (type), video std (id), video std requested (std_req), scode file and number in use. Incorporate said structure into the tuner private data. When checking whether the current firmware needs to be reloaded, first figure out exactly what "type" of firmware we want (base, std and scode), and then proceed to load the appropriate matching base, std-specific and scode records iff there are any changes required. This removes guesswork from the process because we no longer need to individually code a check for every tuning parameter's interactions. 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.c212
1 files changed, 115 insertions, 97 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c
index a5efd5f6e57c..8140d8ad0792 100644
--- a/drivers/media/video/tuner-xc2028.c
+++ b/drivers/media/video/tuner-xc2028.c
@@ -54,6 +54,14 @@ struct firmware_description {
54 unsigned int size; 54 unsigned int size;
55}; 55};
56 56
57struct firmware_properties {
58 unsigned int type;
59 v4l2_std_id id;
60 v4l2_std_id std_req;
61 unsigned int scode_table;
62 int scode_nr;
63};
64
57struct xc2028_data { 65struct xc2028_data {
58 struct list_head xc2028_list; 66 struct list_head xc2028_list;
59 struct tuner_i2c_props i2c_props; 67 struct tuner_i2c_props i2c_props;
@@ -69,14 +77,7 @@ struct xc2028_data {
69 77
70 struct xc2028_ctrl ctrl; 78 struct xc2028_ctrl ctrl;
71 79
72 v4l2_std_id firm_type; /* video stds supported 80 struct firmware_properties cur_fw;
73 by current firmware */
74 fe_bandwidth_t bandwidth; /* Firmware bandwidth:
75 6M, 7M or 8M */
76 int need_load_generic; /* The generic firmware
77 were loaded? */
78 enum tuner_mode mode;
79 struct i2c_client *i2c_client;
80 81
81 struct mutex lock; 82 struct mutex lock;
82}; 83};
@@ -234,7 +235,8 @@ static void free_firmware(struct xc2028_data *priv)
234 235
235 priv->firm = NULL; 236 priv->firm = NULL;
236 priv->firm_size = 0; 237 priv->firm_size = 0;
237 priv->need_load_generic = 1; 238
239 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
238} 240}
239 241
240static int load_all_firmwares(struct dvb_frontend *fe) 242static int load_all_firmwares(struct dvb_frontend *fe)
@@ -393,6 +395,13 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
393 if (((type & ~SCODE) == 0) && (*id == 0)) 395 if (((type & ~SCODE) == 0) && (*id == 0))
394 *id = V4L2_STD_PAL; 396 *id = V4L2_STD_PAL;
395 397
398 if (type & BASE)
399 type &= BASE_TYPES;
400 else if (type & SCODE)
401 type &= SCODE_TYPES;
402 else if (type & DTV_TYPES)
403 type = type & DTV_TYPES;
404
396 /* Seek for exact match */ 405 /* Seek for exact match */
397 for (i = 0; i < priv->firm_size; i++) { 406 for (i = 0; i < priv->firm_size; i++) {
398 if ((type == priv->firm[i].type) && (*id == priv->firm[i].id)) 407 if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
@@ -598,11 +607,10 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
598 v4l2_std_id std, fe_bandwidth_t bandwidth) 607 v4l2_std_id std, fe_bandwidth_t bandwidth)
599{ 608{
600 struct xc2028_data *priv = fe->tuner_priv; 609 struct xc2028_data *priv = fe->tuner_priv;
601 int rc; 610 int rc = 0;
611 unsigned int type = 0;
612 struct firmware_properties new_fw;
602 u16 version, hwmodel; 613 u16 version, hwmodel;
603 v4l2_std_id std0 = 0;
604 unsigned int type0 = 0, type = 0;
605 int change_digital_bandwidth;
606 614
607 tuner_dbg("%s called\n", __FUNCTION__); 615 tuner_dbg("%s called\n", __FUNCTION__);
608 616
@@ -617,61 +625,19 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
617 return rc; 625 return rc;
618 } 626 }
619 627
620 tuner_dbg("I am in mode %u and I should switch to mode %i\n", 628 if (priv->ctrl.type == XC2028_FIRM_MTS)
621 priv->mode, new_mode); 629 type |= MTS;
622 630 if (bandwidth == BANDWIDTH_7_MHZ || bandwidth == BANDWIDTH_8_MHZ)
623 /* first of all, determine whether we have switched the mode */ 631 type |= F8MHZ;
624 if (new_mode != priv->mode) {
625 priv->mode = new_mode;
626 priv->need_load_generic = 1;
627 }
628
629 change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
630 && bandwidth != priv->bandwidth) ? 1 : 0;
631 tuner_dbg("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
632 bandwidth);
633
634 if (priv->need_load_generic) {
635 /* Reset is needed before loading firmware */
636 rc = priv->tuner_callback(priv->video_dev,
637 XC2028_TUNER_RESET, 0);
638 if (rc < 0)
639 return rc;
640
641 type0 = BASE;
642
643 if (priv->ctrl.type == XC2028_FIRM_MTS)
644 type0 |= MTS;
645
646 if (bandwidth == BANDWIDTH_7_MHZ ||
647 bandwidth == BANDWIDTH_8_MHZ)
648 type0 |= F8MHZ;
649
650 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
651
652 rc = load_firmware(fe, type0, &std0);
653 if (rc < 0) {
654 tuner_err("Error %d while loading generic firmware\n",
655 rc);
656 return rc;
657 }
658
659 priv->need_load_generic = 0;
660 priv->firm_type = 0;
661 if (priv->mode == T_DIGITAL_TV)
662 change_digital_bandwidth = 1;
663 }
664 632
665 tuner_dbg("I should change bandwidth %u\n", change_digital_bandwidth); 633 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
666 634
667 if (change_digital_bandwidth) { 635 if (new_mode == T_DIGITAL_TV) {
668 if (priv->ctrl.d2633) 636 if (priv->ctrl.d2633)
669 type |= D2633; 637 type |= D2633;
670 else 638 else
671 type |= D2620; 639 type |= D2620;
672 640
673 /* FIXME: When should select a DTV78 firmware?
674 */
675 switch (bandwidth) { 641 switch (bandwidth) {
676 case BANDWIDTH_8_MHZ: 642 case BANDWIDTH_8_MHZ:
677 type |= DTV8; 643 type |= DTV8;
@@ -683,49 +649,96 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
683 /* FIXME: Should allow select also ATSC */ 649 /* FIXME: Should allow select also ATSC */
684 type |= DTV6 | QAM; 650 type |= DTV6 | QAM;
685 break; 651 break;
686
687 default: 652 default:
688 tuner_err("error: bandwidth not supported.\n"); 653 tuner_err("error: bandwidth not supported.\n");
689 }; 654 };
690 priv->bandwidth = bandwidth;
691 } 655 }
692 656
693 if (!change_digital_bandwidth && priv->mode == T_DIGITAL_TV) 657 new_fw.type = type;
694 return 0; 658 new_fw.id = std;
659 new_fw.std_req = std;
660 new_fw.scode_table = SCODE | priv->ctrl.scode_table;
661 new_fw.scode_nr = 0;
662
663 tuner_dbg("checking firmware, user requested type=");
664 if (debug) {
665 dump_firm_type(new_fw.type);
666 printk("(%x), id %016llx, scode_tbl ", new_fw.type,
667 (unsigned long long)new_fw.std_req);
668 dump_firm_type(priv->ctrl.scode_table);
669 printk("(%x), scode_nr %d\n", priv->ctrl.scode_table,
670 new_fw.scode_nr);
671 }
672
673 /* No need to reload base firmware if it matches */
674 if (((BASE | new_fw.type) & BASE_TYPES) ==
675 (priv->cur_fw.type & BASE_TYPES)) {
676 tuner_dbg("BASE firmware not changed.\n");
677 goto skip_base;
678 }
679
680 /* Updating BASE - forget about all currently loaded firmware */
681 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
682
683 /* Reset is needed before loading firmware */
684 rc = priv->tuner_callback(priv->video_dev,
685 XC2028_TUNER_RESET, 0);
686 if (rc < 0)
687 goto fail;
688
689 rc = load_firmware(fe, BASE | new_fw.type, &new_fw.id);
690 if (rc < 0) {
691 tuner_err("Error %d while loading base firmware\n",
692 rc);
693 goto fail;
694 }
695 695
696 /* Load INIT1, if needed */ 696 /* Load INIT1, if needed */
697 tuner_dbg("Load init1 firmware, if exists\n"); 697 tuner_dbg("Load init1 firmware, if exists\n");
698 type0 = BASE | INIT1;
699 if (priv->ctrl.type == XC2028_FIRM_MTS)
700 type0 |= MTS;
701 698
702 /* FIXME: Should handle errors - if INIT1 found */ 699 rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &new_fw.id);
703 rc = load_firmware(fe, type0, &std0); 700 if (rc < 0 && rc != -ENOENT) {
701 tuner_err("Error %d while loading init1 firmware\n",
702 rc);
703 goto fail;
704 }
704 705
705 /* FIXME: Should add support for FM radio 706skip_base:
707 /*
708 * No need to reload standard specific firmware if base firmware
709 * was not reloaded and requested video standards have not changed.
706 */ 710 */
707 711 if (priv->cur_fw.type == (BASE | new_fw.type) &&
708 if (priv->ctrl.type == XC2028_FIRM_MTS) 712 priv->cur_fw.std_req == std) {
709 type |= MTS;
710
711 if (priv->firm_type & std) {
712 tuner_dbg("Std-specific firmware already loaded.\n"); 713 tuner_dbg("Std-specific firmware already loaded.\n");
713 return 0; 714 goto skip_std_specific;
714 } 715 }
715 716
717 /* Reloading std-specific firmware forces a SCODE update */
718 priv->cur_fw.scode_table = 0;
719
716 /* Add audio hack to std mask */ 720 /* Add audio hack to std mask */
717 std |= parse_audio_std_option(); 721 if (new_mode == T_ANALOG_TV)
722 new_fw.id |= parse_audio_std_option();
718 723
719 rc = load_firmware(fe, type, &std); 724 rc = load_firmware(fe, new_fw.type, &new_fw.id);
720 if (rc < 0) 725 if (rc < 0)
721 return rc; 726 goto fail;
727
728skip_std_specific:
729 if (priv->cur_fw.scode_table == new_fw.scode_table &&
730 priv->cur_fw.scode_nr == new_fw.scode_nr) {
731 tuner_dbg("SCODE firmware already loaded.\n");
732 goto check_device;
733 }
722 734
723 /* Load SCODE firmware, if exists */ 735 /* Load SCODE firmware, if exists */
724 tuner_dbg("Trying to load scode 0\n"); 736 tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
725 type |= SCODE;
726 737
727 rc = load_scode(fe, type, &std, 0); 738 rc = load_scode(fe, new_fw.type | new_fw.scode_table,
739 &new_fw.id, new_fw.scode_nr);
728 740
741check_device:
729 xc2028_get_reg(priv, 0x0004, &version); 742 xc2028_get_reg(priv, 0x0004, &version);
730 xc2028_get_reg(priv, 0x0008, &hwmodel); 743 xc2028_get_reg(priv, 0x0008, &hwmodel);
731 744
@@ -734,9 +747,23 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
734 hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, 747 hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
735 (version & 0xf0) >> 4, version & 0xf); 748 (version & 0xf0) >> 4, version & 0xf);
736 749
737 priv->firm_type = std; 750 memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
751
752 /*
753 * By setting BASE in cur_fw.type only after successfully loading all
754 * firmwares, we can:
755 * 1. Identify that BASE firmware with type=0 has been loaded;
756 * 2. Tell whether BASE firmware was just changed the next time through.
757 */
758 priv->cur_fw.type |= BASE;
738 759
739 return 0; 760 return 0;
761
762fail:
763 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
764 if (rc == -ENOENT)
765 rc = -EINVAL;
766 return rc;
740} 767}
741 768
742static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) 769static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
@@ -785,16 +812,10 @@ static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
785 mutex_lock(&priv->lock); 812 mutex_lock(&priv->lock);
786 813
787 /* HACK: It seems that specific firmware need to be reloaded 814 /* HACK: It seems that specific firmware need to be reloaded
788 when freq is changed */ 815 when watching analog TV and freq is changed */
789 816 if (new_mode != T_DIGITAL_TV)
790 priv->firm_type = 0; 817 priv->cur_fw.type = 0;
791
792 /* Reset GPIO 1 */
793 rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
794 if (rc < 0)
795 goto ret;
796 818
797 msleep(10);
798 tuner_dbg("should set frequency %d kHz\n", freq / 1000); 819 tuner_dbg("should set frequency %d kHz\n", freq / 1000);
799 820
800 if (check_firmware(fe, new_mode, std, bandwidth) < 0) 821 if (check_firmware(fe, new_mode, std, bandwidth) < 0)
@@ -802,7 +823,7 @@ static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
802 823
803 if (new_mode == T_DIGITAL_TV) { 824 if (new_mode == T_DIGITAL_TV) {
804 offset = 2750000; 825 offset = 2750000;
805 if (priv->bandwidth == BANDWIDTH_7_MHZ) 826 if (priv->cur_fw.type & DTV7)
806 offset -= 500000; 827 offset -= 500000;
807 } 828 }
808 829
@@ -994,9 +1015,6 @@ void *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg)
994 return NULL; 1015 return NULL;
995 } 1016 }
996 1017
997 priv->bandwidth = BANDWIDTH_6_MHZ;
998 priv->need_load_generic = 1;
999 priv->mode = T_UNINITIALIZED;
1000 priv->i2c_props.addr = cfg->i2c_addr; 1018 priv->i2c_props.addr = cfg->i2c_addr;
1001 priv->i2c_props.adap = cfg->i2c_adap; 1019 priv->i2c_props.adap = cfg->i2c_adap;
1002 priv->video_dev = video_dev; 1020 priv->video_dev = video_dev;