diff options
author | Michael Krufky <mkrufky@linuxtv.org> | 2008-04-22 13:45:52 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-04-24 13:07:49 -0400 |
commit | 62325497db6ef3b13cae41d5038e2693997d7d3e (patch) | |
tree | edd38f653b28734ea22864a652ce17abebe84209 /drivers | |
parent | ac8b63b30a320699e602a18af6101528b408d41d (diff) |
V4L/DVB (7347): tuner-simple: add basic support for digital tuning of hybrid devices
Add entry points used for digital tuning via the dvb_frontend.
Share state data between multiple instances of the driver for hybrid
tuners.
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/tuner-simple.c | 156 |
1 files changed, 146 insertions, 10 deletions
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index ee5ef860700a..d6d922c32bce 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c | |||
@@ -88,14 +88,20 @@ MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner"); | |||
88 | #define TUNER_PLL_LOCKED 0x40 | 88 | #define TUNER_PLL_LOCKED 0x40 |
89 | #define TUNER_STEREO_MK3 0x04 | 89 | #define TUNER_STEREO_MK3 0x04 |
90 | 90 | ||
91 | static DEFINE_MUTEX(tuner_simple_list_mutex); | ||
92 | static LIST_HEAD(hybrid_tuner_instance_list); | ||
93 | |||
91 | struct tuner_simple_priv { | 94 | struct tuner_simple_priv { |
92 | u16 last_div; | 95 | u16 last_div; |
96 | |||
93 | struct tuner_i2c_props i2c_props; | 97 | struct tuner_i2c_props i2c_props; |
98 | struct list_head hybrid_tuner_instance_list; | ||
94 | 99 | ||
95 | unsigned int type; | 100 | unsigned int type; |
96 | struct tunertype *tun; | 101 | struct tunertype *tun; |
97 | 102 | ||
98 | u32 frequency; | 103 | u32 frequency; |
104 | u32 bandwidth; | ||
99 | }; | 105 | }; |
100 | 106 | ||
101 | /* ---------------------------------------------------------------------- */ | 107 | /* ---------------------------------------------------------------------- */ |
@@ -188,6 +194,9 @@ static inline char *tuner_param_name(enum param_type type) | |||
188 | case TUNER_PARAM_TYPE_NTSC: | 194 | case TUNER_PARAM_TYPE_NTSC: |
189 | name = "ntsc"; | 195 | name = "ntsc"; |
190 | break; | 196 | break; |
197 | case TUNER_PARAM_TYPE_DIGITAL: | ||
198 | name = "digital"; | ||
199 | break; | ||
191 | default: | 200 | default: |
192 | name = "unknown"; | 201 | name = "unknown"; |
193 | break; | 202 | break; |
@@ -687,14 +696,118 @@ static int simple_set_params(struct dvb_frontend *fe, | |||
687 | priv->frequency = params->frequency * 62500; | 696 | priv->frequency = params->frequency * 62500; |
688 | break; | 697 | break; |
689 | } | 698 | } |
699 | priv->bandwidth = 0; | ||
690 | 700 | ||
691 | return ret; | 701 | return ret; |
692 | } | 702 | } |
693 | 703 | ||
704 | static int simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, | ||
705 | const struct dvb_frontend_parameters *params) | ||
706 | { | ||
707 | struct tuner_simple_priv *priv = fe->tuner_priv; | ||
708 | struct tunertype *tun = priv->tun; | ||
709 | static struct tuner_params *t_params; | ||
710 | u8 config, cb; | ||
711 | u32 div; | ||
712 | int ret, frequency = params->frequency / 62500; | ||
713 | |||
714 | t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); | ||
715 | ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); | ||
716 | if (ret < 0) | ||
717 | return ret; | ||
718 | |||
719 | div = ((frequency + t_params->iffreq) * 62500 + offset + | ||
720 | tun->stepsize/2) / tun->stepsize; | ||
721 | |||
722 | buf[0] = div >> 8; | ||
723 | buf[1] = div & 0xff; | ||
724 | buf[2] = config; | ||
725 | buf[3] = cb; | ||
726 | |||
727 | tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", | ||
728 | tun->name, div, buf[0], buf[1], buf[2], buf[3]); | ||
729 | |||
730 | /* calculate the frequency we set it to */ | ||
731 | return (div * tun->stepsize) - t_params->iffreq; | ||
732 | } | ||
733 | |||
734 | static int simple_dvb_calc_regs(struct dvb_frontend *fe, | ||
735 | struct dvb_frontend_parameters *params, | ||
736 | u8 *buf, int buf_len) | ||
737 | { | ||
738 | struct tuner_simple_priv *priv = fe->tuner_priv; | ||
739 | int ret; | ||
740 | u32 frequency; | ||
741 | |||
742 | if (buf_len < 5) | ||
743 | return -EINVAL; | ||
744 | |||
745 | ret = simple_dvb_configure(fe, buf+1, params); | ||
746 | if (ret < 0) | ||
747 | return ret; | ||
748 | else | ||
749 | frequency = ret; | ||
750 | |||
751 | buf[0] = priv->i2c_props.addr; | ||
752 | |||
753 | priv->frequency = frequency; | ||
754 | priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? | ||
755 | params->u.ofdm.bandwidth : 0; | ||
756 | |||
757 | return 5; | ||
758 | } | ||
759 | |||
760 | static int simple_dvb_set_params(struct dvb_frontend *fe, | ||
761 | struct dvb_frontend_parameters *params) | ||
762 | { | ||
763 | struct tuner_simple_priv *priv = fe->tuner_priv; | ||
764 | u32 prev_freq, prev_bw; | ||
765 | int ret; | ||
766 | u8 buf[5]; | ||
767 | |||
768 | if (priv->i2c_props.adap == NULL) | ||
769 | return -EINVAL; | ||
770 | |||
771 | prev_freq = priv->frequency; | ||
772 | prev_bw = priv->bandwidth; | ||
773 | |||
774 | ret = simple_dvb_calc_regs(fe, params, buf, 5); | ||
775 | if (ret != 5) | ||
776 | goto fail; | ||
777 | |||
778 | /* put analog demod in standby when tuning digital */ | ||
779 | if (fe->ops.analog_ops.standby) | ||
780 | fe->ops.analog_ops.standby(fe); | ||
781 | |||
782 | if (fe->ops.i2c_gate_ctrl) | ||
783 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
784 | |||
785 | /* buf[0] contains the i2c address, but * | ||
786 | * we already have it in i2c_props.addr */ | ||
787 | ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4); | ||
788 | if (ret != 4) | ||
789 | goto fail; | ||
790 | |||
791 | return 0; | ||
792 | fail: | ||
793 | /* calc_regs sets frequency and bandwidth. if we failed, unset them */ | ||
794 | priv->frequency = prev_freq; | ||
795 | priv->bandwidth = prev_bw; | ||
796 | |||
797 | return ret; | ||
798 | } | ||
694 | 799 | ||
695 | static int simple_release(struct dvb_frontend *fe) | 800 | static int simple_release(struct dvb_frontend *fe) |
696 | { | 801 | { |
697 | kfree(fe->tuner_priv); | 802 | struct tuner_simple_priv *priv = fe->tuner_priv; |
803 | |||
804 | mutex_lock(&tuner_simple_list_mutex); | ||
805 | |||
806 | if (priv) | ||
807 | hybrid_tuner_release_state(priv); | ||
808 | |||
809 | mutex_unlock(&tuner_simple_list_mutex); | ||
810 | |||
698 | fe->tuner_priv = NULL; | 811 | fe->tuner_priv = NULL; |
699 | 812 | ||
700 | return 0; | 813 | return 0; |
@@ -707,10 +820,20 @@ static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency) | |||
707 | return 0; | 820 | return 0; |
708 | } | 821 | } |
709 | 822 | ||
823 | static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) | ||
824 | { | ||
825 | struct tuner_simple_priv *priv = fe->tuner_priv; | ||
826 | *bandwidth = priv->bandwidth; | ||
827 | return 0; | ||
828 | } | ||
829 | |||
710 | static struct dvb_tuner_ops simple_tuner_ops = { | 830 | static struct dvb_tuner_ops simple_tuner_ops = { |
711 | .set_analog_params = simple_set_params, | 831 | .set_analog_params = simple_set_params, |
832 | .set_params = simple_dvb_set_params, | ||
833 | .calc_regs = simple_dvb_calc_regs, | ||
712 | .release = simple_release, | 834 | .release = simple_release, |
713 | .get_frequency = simple_get_frequency, | 835 | .get_frequency = simple_get_frequency, |
836 | .get_bandwidth = simple_get_bandwidth, | ||
714 | .get_status = simple_get_status, | 837 | .get_status = simple_get_status, |
715 | .get_rf_strength = simple_get_rf_strength, | 838 | .get_rf_strength = simple_get_rf_strength, |
716 | }; | 839 | }; |
@@ -721,6 +844,7 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, | |||
721 | unsigned int type) | 844 | unsigned int type) |
722 | { | 845 | { |
723 | struct tuner_simple_priv *priv = NULL; | 846 | struct tuner_simple_priv *priv = NULL; |
847 | int instance; | ||
724 | 848 | ||
725 | if (type >= tuner_count) { | 849 | if (type >= tuner_count) { |
726 | printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", | 850 | printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", |
@@ -728,22 +852,34 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, | |||
728 | return NULL; | 852 | return NULL; |
729 | } | 853 | } |
730 | 854 | ||
731 | priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL); | 855 | mutex_lock(&tuner_simple_list_mutex); |
732 | if (priv == NULL) | 856 | |
857 | instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv, | ||
858 | hybrid_tuner_instance_list, | ||
859 | i2c_adap, i2c_addr, | ||
860 | "tuner-simple"); | ||
861 | switch (instance) { | ||
862 | case 0: | ||
863 | mutex_unlock(&tuner_simple_list_mutex); | ||
733 | return NULL; | 864 | return NULL; |
734 | fe->tuner_priv = priv; | 865 | break; |
866 | case 1: | ||
867 | fe->tuner_priv = priv; | ||
735 | 868 | ||
736 | priv->i2c_props.addr = i2c_addr; | 869 | priv->type = type; |
737 | priv->i2c_props.adap = i2c_adap; | 870 | priv->tun = &tuners[type]; |
738 | priv->i2c_props.name = "tuner-simple"; | 871 | break; |
872 | default: | ||
873 | fe->tuner_priv = priv; | ||
874 | break; | ||
875 | } | ||
739 | 876 | ||
740 | priv->type = type; | 877 | mutex_unlock(&tuner_simple_list_mutex); |
741 | priv->tun = &tuners[type]; | ||
742 | 878 | ||
743 | memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, | 879 | memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, |
744 | sizeof(struct dvb_tuner_ops)); | 880 | sizeof(struct dvb_tuner_ops)); |
745 | 881 | ||
746 | tuner_info("type set to %d (%s)\n", priv->type, priv->tun->name); | 882 | tuner_info("type set to %d (%s)\n", type, priv->tun->name); |
747 | 883 | ||
748 | strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, | 884 | strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, |
749 | sizeof(fe->ops.tuner_ops.info.name)); | 885 | sizeof(fe->ops.tuner_ops.info.name)); |