diff options
author | Chris Pascoe <c.pascoe@itee.uq.edu.au> | 2007-11-19 09:22:03 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:02:30 -0500 |
commit | e0f0b37a3e624440b1b0e8a5978b367895226e75 (patch) | |
tree | 98135357e75ca922b093996b3e5a15d68978c8f0 /drivers/media/video/tuner-xc2028.c | |
parent | 59a636e50f339f91880b3a1e395829c43cc6541a (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.c | 212 |
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 | ||
57 | struct 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 | |||
57 | struct xc2028_data { | 65 | struct 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 | ||
240 | static int load_all_firmwares(struct dvb_frontend *fe) | 242 | static 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 | 706 | skip_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 | |||
728 | skip_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 | ||
741 | check_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 | |||
762 | fail: | ||
763 | memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); | ||
764 | if (rc == -ENOENT) | ||
765 | rc = -EINVAL; | ||
766 | return rc; | ||
740 | } | 767 | } |
741 | 768 | ||
742 | static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) | 769 | static 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; |