diff options
author | Manu Abraham <abraham.manu@gmail.com> | 2007-09-22 12:30:09 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-29 14:53:15 -0500 |
commit | 00360205b94cb9d0b93be5f4ca6676b53dceba7f (patch) | |
tree | 6b3bde604170c02a1404e86c7ffe25d61e9f699d /drivers/media/dvb/frontends | |
parent | 43498ade8ade7cee1b983e5410c838ac84eef691 (diff) |
V4L/DVB (9392): initial go at TDA8261 tuner
Signed-off-by: Manu Abraham <manu@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb/frontends')
-rw-r--r-- | drivers/media/dvb/frontends/tda8261.c | 217 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/tda8261.h | 25 |
2 files changed, 242 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c new file mode 100644 index 000000000000..dc7dc025ceb7 --- /dev/null +++ b/drivers/media/dvb/frontends/tda8261.c | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | TDA8261 8PSK/QPSK tuner driver | ||
3 | Copyright (C) Manu Abraham (abraham.manu@gmail.com) | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; if not, write to the Free Software | ||
17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | |||
25 | #include "dvb_frontend.h" | ||
26 | #include "tda8261.h" | ||
27 | |||
28 | struct tda8261_state { | ||
29 | struct dvb_frontend *fe; | ||
30 | struct i2c_adapter *i2c; | ||
31 | struct tda8261_config *config; | ||
32 | |||
33 | /* state cache */ | ||
34 | u32 frequency; | ||
35 | u32 bandwidth; | ||
36 | }; | ||
37 | |||
38 | static int tda8261_read(struct tda8261_state *state, u8 *buf) | ||
39 | { | ||
40 | struct tda8261_config *config = state->config; | ||
41 | int err = 0; | ||
42 | struct i2c_msg msg[] = { | ||
43 | { .addr = config->addr, .flags = 0, .buf = NULL, .len = 0 }, | ||
44 | { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 } | ||
45 | }; | ||
46 | |||
47 | if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) | ||
48 | printk("%s: read error, err=%d\n", __func__, err); | ||
49 | |||
50 | return err; | ||
51 | } | ||
52 | |||
53 | static int tda8261_write(struct tda8261_state *state, u8 *buf) | ||
54 | { | ||
55 | struct tda8261_config *config = state->config; | ||
56 | int err = 0; | ||
57 | struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 }; | ||
58 | |||
59 | if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) | ||
60 | printk("%s: read error, err=%d\n", __func__, err); | ||
61 | |||
62 | return err; | ||
63 | } | ||
64 | |||
65 | static int tda8261_get_status(struct dvb_frontend *fe, u32 *status) | ||
66 | { | ||
67 | struct tda8261_state *state = fe->tuner_priv; | ||
68 | u8 result = 0; | ||
69 | int err = 0; | ||
70 | |||
71 | if ((err = tda8261_read(state, &result)) < 0) { | ||
72 | printk("%s: I/O Error\n", __func__); | ||
73 | return err; | ||
74 | } | ||
75 | if ((result >> 6) & 0x01) { | ||
76 | printk("%s: Tuner Phase Locked\n", __func__); | ||
77 | *status = 1; | ||
78 | } | ||
79 | |||
80 | return err; | ||
81 | } | ||
82 | |||
83 | static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */ | ||
84 | static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 }; | ||
85 | |||
86 | static int tda8261_get_state(struct dvb_frontend *fe, | ||
87 | enum tuner_param param, | ||
88 | struct tuner_state *tstate) | ||
89 | { | ||
90 | struct tda8261_state *state = fe->tuner_priv; | ||
91 | int err = 0; | ||
92 | |||
93 | switch (param) { | ||
94 | case DVBFE_TUNER_FREQUENCY: | ||
95 | tstate->frequency = state->frequency; | ||
96 | break; | ||
97 | case DVBFE_TUNER_BANDWIDTH: | ||
98 | tstate->bandwidth = 60000000; /* FIXME! need to calculate Bandwidth */ | ||
99 | break; | ||
100 | default: | ||
101 | printk("%s: Unknown parameter (param=%d)\n", __func__, param); | ||
102 | err = -EINVAL; | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | return err; | ||
107 | } | ||
108 | |||
109 | static int tda8261_set_state(struct dvb_frontend *fe, | ||
110 | enum tuner_param param, | ||
111 | struct tuner_state *tstate) | ||
112 | { | ||
113 | struct tda8261_state *state = fe->tuner_priv; | ||
114 | struct tda8261_config *config = state->config; | ||
115 | u32 frequency, N, status = 0; | ||
116 | u8 buf[4]; | ||
117 | int err = 0; | ||
118 | |||
119 | if (param & DVBFE_TUNER_FREQUENCY) { | ||
120 | /** | ||
121 | * N = Max VCO Frequency / Channel Spacing | ||
122 | * Max VCO Frequency = VCO frequency + (channel spacing - 1) | ||
123 | * (to account for half channel spacing on either side) | ||
124 | */ | ||
125 | frequency = tstate->frequency; | ||
126 | N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size]; | ||
127 | buf[0] = (N >> 8) & 0xff; | ||
128 | buf[1] = N & 0xff; | ||
129 | buf[2] = (0x08 << 4) | ((ref_div[config->step_size] & 0x07) << 1); | ||
130 | buf[3] = 0xc0; | ||
131 | /* Set params */ | ||
132 | printk("%s: Frequency=%d, Sending[ %02x %02x %02x %02x ]\n", | ||
133 | __func__, frequency, buf[0], buf[1], buf[2], buf[3]); | ||
134 | |||
135 | if ((err = tda8261_write(state, buf)) < 0) { | ||
136 | printk("%s: I/O Error\n", __func__); | ||
137 | return err; | ||
138 | } | ||
139 | /* sleep for some time */ | ||
140 | msleep(100); | ||
141 | /* check status */ | ||
142 | if ((err = tda8261_get_status(fe, &status)) < 0) { | ||
143 | printk("%s: I/O Error\n", __func__); | ||
144 | return err; | ||
145 | } | ||
146 | if (status == 1) | ||
147 | printk("%s: Tuner Phase locked: status=%d\n", __func__, status); | ||
148 | else | ||
149 | printk("%s: No Phase lock: status=%d\n", __func__, status); | ||
150 | } else { | ||
151 | printk("%s: Unknown parameter (param=%d)\n", __func__, param); | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int tda8261_release(struct dvb_frontend *fe) | ||
159 | { | ||
160 | struct tda8261_state *state = fe->tuner_priv; | ||
161 | |||
162 | fe->tuner_priv = NULL; | ||
163 | kfree(state); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct dvb_tuner_ops tda8261_ops = { | ||
168 | |||
169 | .info = { | ||
170 | .name = "TDA8261", | ||
171 | // .tuner_name = NULL, | ||
172 | .frequency_min = 950000, | ||
173 | .frequency_max = 2150000, | ||
174 | .frequency_step = 0 | ||
175 | }, | ||
176 | |||
177 | .set_state = tda8261_set_state, | ||
178 | .get_state = tda8261_get_state, | ||
179 | .release = tda8261_release | ||
180 | }; | ||
181 | |||
182 | struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, | ||
183 | struct tda8261_config *config, | ||
184 | struct i2c_adapter *i2c) | ||
185 | { | ||
186 | struct tda8261_state *state = NULL; | ||
187 | |||
188 | if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL) | ||
189 | goto exit; | ||
190 | |||
191 | state->config = config; | ||
192 | state->i2c = i2c; | ||
193 | state->fe = fe; | ||
194 | fe->tuner_priv = state; | ||
195 | fe->ops.tuner_ops = tda8261_ops; | ||
196 | |||
197 | fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; | ||
198 | // fe->ops.tuner_ops.tuner_name = &config->buf; | ||
199 | |||
200 | // printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n", | ||
201 | // __func__, fe->ops.tuner_ops.tuner_name); | ||
202 | printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); | ||
203 | |||
204 | |||
205 | return fe; | ||
206 | |||
207 | exit: | ||
208 | kfree(state); | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
212 | EXPORT_SYMBOL(tda8261_attach); | ||
213 | MODULE_PARM_DESC(verbose, "Set verbosity level"); | ||
214 | |||
215 | MODULE_AUTHOR("Manu Abraham"); | ||
216 | MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); | ||
217 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb/frontends/tda8261.h new file mode 100644 index 000000000000..b8d8e37b045b --- /dev/null +++ b/drivers/media/dvb/frontends/tda8261.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef __TDA8261_H | ||
2 | #define __TDA8261_H | ||
3 | |||
4 | enum tda8261_step { | ||
5 | TDA8261_STEP_2000 = 0, /* 2000 kHz */ | ||
6 | TDA8261_STEP_1000, /* 1000 kHz */ | ||
7 | TDA8261_STEP_500, /* 500 kHz */ | ||
8 | TDA8261_STEP_250, /* 250 kHz */ | ||
9 | TDA8261_STEP_125 /* 125 kHz */ | ||
10 | }; | ||
11 | |||
12 | struct tda8261_config { | ||
13 | // u8 buf[16]; | ||
14 | u8 addr; | ||
15 | enum tda8261_step step_size; | ||
16 | }; | ||
17 | |||
18 | /* move out from here! */ | ||
19 | static const struct tda8261_config sd1878c_config = { | ||
20 | // .name = "SD1878C", | ||
21 | .addr = 0x60, | ||
22 | .step_size = TDA8261_STEP_1000 /* kHz */ | ||
23 | }; | ||
24 | |||
25 | #endif// __TDA8261_H | ||