diff options
author | Konstantin Dimitrov <kosio.dimitrov@gmail.com> | 2012-12-23 17:25:27 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-12-27 16:26:58 -0500 |
commit | 6fef4fc71e79282b673d7613cfc63da6bdeec5bd (patch) | |
tree | 99a3a5593b9c42ce2590c67578fbb86344929128 | |
parent | c1965eae65f0db2eee574f72aab4e8b34ecf8f9c (diff) |
[media] ts2020: add ts2020 tuner driver
add separate ts2020 tuner driver
Signed-off-by: Konstantin Dimitrov <kosio.dimitrov@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/dvb-frontends/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/ts2020.c | 323 | ||||
-rw-r--r-- | drivers/media/dvb-frontends/ts2020.h | 49 |
4 files changed, 380 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5efec73a32d2..6f809a70c78e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig | |||
@@ -207,6 +207,13 @@ config DVB_SI21XX | |||
207 | help | 207 | help |
208 | A DVB-S tuner module. Say Y when you want to support this frontend. | 208 | A DVB-S tuner module. Say Y when you want to support this frontend. |
209 | 209 | ||
210 | config DVB_TS2020 | ||
211 | tristate "Montage Tehnology TS2020 based tuners" | ||
212 | depends on DVB_CORE && I2C | ||
213 | default m if DVB_FE_CUSTOMISE | ||
214 | help | ||
215 | A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. | ||
216 | |||
210 | config DVB_DS3000 | 217 | config DVB_DS3000 |
211 | tristate "Montage Tehnology DS3000 based" | 218 | tristate "Montage Tehnology DS3000 based" |
212 | depends on DVB_CORE && I2C | 219 | depends on DVB_CORE && I2C |
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 7eb73bbd2e26..cebc0faffab5 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile | |||
@@ -88,6 +88,7 @@ obj-$(CONFIG_DVB_ISL6423) += isl6423.o | |||
88 | obj-$(CONFIG_DVB_EC100) += ec100.o | 88 | obj-$(CONFIG_DVB_EC100) += ec100.o |
89 | obj-$(CONFIG_DVB_HD29L2) += hd29l2.o | 89 | obj-$(CONFIG_DVB_HD29L2) += hd29l2.o |
90 | obj-$(CONFIG_DVB_DS3000) += ds3000.o | 90 | obj-$(CONFIG_DVB_DS3000) += ds3000.o |
91 | obj-$(CONFIG_DVB_TS2020) += ts2020.o | ||
91 | obj-$(CONFIG_DVB_MB86A16) += mb86a16.o | 92 | obj-$(CONFIG_DVB_MB86A16) += mb86a16.o |
92 | obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o | 93 | obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o |
93 | obj-$(CONFIG_DVB_IX2505V) += ix2505v.o | 94 | obj-$(CONFIG_DVB_IX2505V) += ix2505v.o |
diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c new file mode 100644 index 000000000000..8dce4ae55f09 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.c | |||
@@ -0,0 +1,323 @@ | |||
1 | /* | ||
2 | Montage Technology TS2020 - Silicon Tuner driver | ||
3 | Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> | ||
4 | |||
5 | Copyright (C) 2009-2012 TurboSight.com | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; if not, write to the Free Software | ||
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include "dvb_frontend.h" | ||
23 | #include "ts2020.h" | ||
24 | |||
25 | #define TS2020_XTAL_FREQ 27000 /* in kHz */ | ||
26 | |||
27 | struct ts2020_state { | ||
28 | u8 tuner_address; | ||
29 | struct i2c_adapter *i2c; | ||
30 | }; | ||
31 | |||
32 | static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) | ||
33 | { | ||
34 | struct ts2020_state *state = fe->tuner_priv; | ||
35 | |||
36 | int ret; | ||
37 | u8 b0[] = { reg }; | ||
38 | u8 b1[] = { 0 }; | ||
39 | struct i2c_msg msg[] = { | ||
40 | { | ||
41 | .addr = state->tuner_address, | ||
42 | .flags = 0, | ||
43 | .buf = b0, | ||
44 | .len = 1 | ||
45 | }, { | ||
46 | .addr = state->tuner_address, | ||
47 | .flags = I2C_M_RD, | ||
48 | .buf = b1, | ||
49 | .len = 1 | ||
50 | } | ||
51 | }; | ||
52 | |||
53 | if (fe->ops.i2c_gate_ctrl) | ||
54 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
55 | |||
56 | ret = i2c_transfer(state->i2c, msg, 2); | ||
57 | |||
58 | if (fe->ops.i2c_gate_ctrl) | ||
59 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
60 | |||
61 | if (ret != 2) { | ||
62 | printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); | ||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | return b1[0]; | ||
67 | } | ||
68 | |||
69 | static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) | ||
70 | { | ||
71 | struct ts2020_state *state = fe->tuner_priv; | ||
72 | |||
73 | u8 buf[] = { reg, data }; | ||
74 | struct i2c_msg msg = { .addr = state->tuner_address, | ||
75 | .flags = 0, .buf = buf, .len = 2 }; | ||
76 | int err; | ||
77 | |||
78 | |||
79 | if (fe->ops.i2c_gate_ctrl) | ||
80 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
81 | |||
82 | err = i2c_transfer(state->i2c, &msg, 1); | ||
83 | |||
84 | if (fe->ops.i2c_gate_ctrl) | ||
85 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
86 | |||
87 | if (err != 1) { | ||
88 | printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," | ||
89 | " value == 0x%02x)\n", __func__, err, reg, data); | ||
90 | return -EREMOTEIO; | ||
91 | } | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int ts2020_init(struct dvb_frontend *fe) | ||
97 | { | ||
98 | ts2020_writereg(fe, 0x42, 0x73); | ||
99 | ts2020_writereg(fe, 0x05, 0x01); | ||
100 | ts2020_writereg(fe, 0x62, 0xf5); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
105 | { | ||
106 | u16 ndiv, div4; | ||
107 | |||
108 | div4 = (ts2020_readreg(fe, 0x10) & 0x10) >> 4; | ||
109 | |||
110 | ndiv = ts2020_readreg(fe, 0x01); | ||
111 | ndiv &= 0x0f; | ||
112 | ndiv <<= 8; | ||
113 | ndiv |= ts2020_readreg(fe, 0x02); | ||
114 | |||
115 | /* actual tuned frequency, i.e. including the offset */ | ||
116 | *frequency = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ | ||
117 | / (6 + 8) / (div4 + 1) / 2; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int ts2020_set_params(struct dvb_frontend *fe) | ||
123 | { | ||
124 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
125 | |||
126 | u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; | ||
127 | u16 value, ndiv; | ||
128 | u32 srate = 0, f3db; | ||
129 | |||
130 | ts2020_init(fe); | ||
131 | |||
132 | /* unknown */ | ||
133 | ts2020_writereg(fe, 0x07, 0x02); | ||
134 | ts2020_writereg(fe, 0x10, 0x00); | ||
135 | ts2020_writereg(fe, 0x60, 0x79); | ||
136 | ts2020_writereg(fe, 0x08, 0x01); | ||
137 | ts2020_writereg(fe, 0x00, 0x01); | ||
138 | div4 = 0; | ||
139 | |||
140 | /* calculate and set freq divider */ | ||
141 | if (c->frequency < 1146000) { | ||
142 | ts2020_writereg(fe, 0x10, 0x11); | ||
143 | div4 = 1; | ||
144 | ndiv = ((c->frequency * (6 + 8) * 4) + | ||
145 | (TS2020_XTAL_FREQ / 2)) / | ||
146 | TS2020_XTAL_FREQ - 1024; | ||
147 | } else { | ||
148 | ts2020_writereg(fe, 0x10, 0x01); | ||
149 | ndiv = ((c->frequency * (6 + 8) * 2) + | ||
150 | (TS2020_XTAL_FREQ / 2)) / | ||
151 | TS2020_XTAL_FREQ - 1024; | ||
152 | } | ||
153 | |||
154 | ts2020_writereg(fe, 0x01, (ndiv & 0x0f00) >> 8); | ||
155 | ts2020_writereg(fe, 0x02, ndiv & 0x00ff); | ||
156 | |||
157 | /* set pll */ | ||
158 | ts2020_writereg(fe, 0x03, 0x06); | ||
159 | ts2020_writereg(fe, 0x51, 0x0f); | ||
160 | ts2020_writereg(fe, 0x51, 0x1f); | ||
161 | ts2020_writereg(fe, 0x50, 0x10); | ||
162 | ts2020_writereg(fe, 0x50, 0x00); | ||
163 | msleep(5); | ||
164 | |||
165 | /* unknown */ | ||
166 | ts2020_writereg(fe, 0x51, 0x17); | ||
167 | ts2020_writereg(fe, 0x51, 0x1f); | ||
168 | ts2020_writereg(fe, 0x50, 0x08); | ||
169 | ts2020_writereg(fe, 0x50, 0x00); | ||
170 | msleep(5); | ||
171 | |||
172 | value = ts2020_readreg(fe, 0x3d); | ||
173 | value &= 0x0f; | ||
174 | if ((value > 4) && (value < 15)) { | ||
175 | value -= 3; | ||
176 | if (value < 4) | ||
177 | value = 4; | ||
178 | value = ((value << 3) | 0x01) & 0x79; | ||
179 | } | ||
180 | |||
181 | ts2020_writereg(fe, 0x60, value); | ||
182 | ts2020_writereg(fe, 0x51, 0x17); | ||
183 | ts2020_writereg(fe, 0x51, 0x1f); | ||
184 | ts2020_writereg(fe, 0x50, 0x08); | ||
185 | ts2020_writereg(fe, 0x50, 0x00); | ||
186 | |||
187 | /* set low-pass filter period */ | ||
188 | ts2020_writereg(fe, 0x04, 0x2e); | ||
189 | ts2020_writereg(fe, 0x51, 0x1b); | ||
190 | ts2020_writereg(fe, 0x51, 0x1f); | ||
191 | ts2020_writereg(fe, 0x50, 0x04); | ||
192 | ts2020_writereg(fe, 0x50, 0x00); | ||
193 | msleep(5); | ||
194 | |||
195 | srate = c->symbol_rate / 1000; | ||
196 | |||
197 | f3db = (srate << 2) / 5 + 2000; | ||
198 | if (srate < 5000) | ||
199 | f3db += 3000; | ||
200 | if (f3db < 7000) | ||
201 | f3db = 7000; | ||
202 | if (f3db > 40000) | ||
203 | f3db = 40000; | ||
204 | |||
205 | /* set low-pass filter baseband */ | ||
206 | value = ts2020_readreg(fe, 0x26); | ||
207 | mlpf = 0x2e * 207 / ((value << 1) + 151); | ||
208 | mlpf_max = mlpf * 135 / 100; | ||
209 | mlpf_min = mlpf * 78 / 100; | ||
210 | if (mlpf_max > 63) | ||
211 | mlpf_max = 63; | ||
212 | |||
213 | /* rounded to the closest integer */ | ||
214 | nlpf = ((mlpf * f3db * 1000) + (2766 * TS2020_XTAL_FREQ / 2)) | ||
215 | / (2766 * TS2020_XTAL_FREQ); | ||
216 | if (nlpf > 23) | ||
217 | nlpf = 23; | ||
218 | if (nlpf < 1) | ||
219 | nlpf = 1; | ||
220 | |||
221 | /* rounded to the closest integer */ | ||
222 | mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + | ||
223 | (1000 * f3db / 2)) / (1000 * f3db); | ||
224 | |||
225 | if (mlpf_new < mlpf_min) { | ||
226 | nlpf++; | ||
227 | mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + | ||
228 | (1000 * f3db / 2)) / (1000 * f3db); | ||
229 | } | ||
230 | |||
231 | if (mlpf_new > mlpf_max) | ||
232 | mlpf_new = mlpf_max; | ||
233 | |||
234 | ts2020_writereg(fe, 0x04, mlpf_new); | ||
235 | ts2020_writereg(fe, 0x06, nlpf); | ||
236 | ts2020_writereg(fe, 0x51, 0x1b); | ||
237 | ts2020_writereg(fe, 0x51, 0x1f); | ||
238 | ts2020_writereg(fe, 0x50, 0x04); | ||
239 | ts2020_writereg(fe, 0x50, 0x00); | ||
240 | msleep(5); | ||
241 | |||
242 | /* unknown */ | ||
243 | ts2020_writereg(fe, 0x51, 0x1e); | ||
244 | ts2020_writereg(fe, 0x51, 0x1f); | ||
245 | ts2020_writereg(fe, 0x50, 0x01); | ||
246 | ts2020_writereg(fe, 0x50, 0x00); | ||
247 | msleep(60); | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static int ts2020_release(struct dvb_frontend *fe) | ||
253 | { | ||
254 | struct ts2020_state *state = fe->tuner_priv; | ||
255 | |||
256 | fe->tuner_priv = NULL; | ||
257 | kfree(state); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | int ts2020_get_signal_strength(struct dvb_frontend *fe, | ||
263 | u16 *signal_strength) | ||
264 | { | ||
265 | u16 sig_reading, sig_strength; | ||
266 | u8 rfgain, bbgain; | ||
267 | |||
268 | rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; | ||
269 | bbgain = ts2020_readreg(fe, 0x21) & 0x1f; | ||
270 | |||
271 | if (rfgain > 15) | ||
272 | rfgain = 15; | ||
273 | if (bbgain > 13) | ||
274 | bbgain = 13; | ||
275 | |||
276 | sig_reading = rfgain * 2 + bbgain * 3; | ||
277 | |||
278 | sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; | ||
279 | |||
280 | /* cook the value to be suitable for szap-s2 human readable output */ | ||
281 | *signal_strength = sig_strength * 1000; | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static struct dvb_tuner_ops ts2020_ops = { | ||
287 | .info = { | ||
288 | .name = "Montage Technology TS2020 Silicon Tuner", | ||
289 | .frequency_min = 950000, | ||
290 | .frequency_max = 2150000, | ||
291 | }, | ||
292 | |||
293 | .init = ts2020_init, | ||
294 | .release = ts2020_release, | ||
295 | .set_params = ts2020_set_params, | ||
296 | .get_frequency = ts2020_get_frequency, | ||
297 | .get_rf_strength = ts2020_get_signal_strength | ||
298 | }; | ||
299 | |||
300 | struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, | ||
301 | const struct ts2020_config *config, struct i2c_adapter *i2c) | ||
302 | { | ||
303 | struct ts2020_state *state = NULL; | ||
304 | |||
305 | /* allocate memory for the internal state */ | ||
306 | state = kzalloc(sizeof(struct ts2020_state), GFP_KERNEL); | ||
307 | if (!state) | ||
308 | return NULL; | ||
309 | |||
310 | /* setup the state */ | ||
311 | state->tuner_address = config->tuner_address; | ||
312 | state->i2c = i2c; | ||
313 | fe->tuner_priv = state; | ||
314 | fe->ops.tuner_ops = ts2020_ops; | ||
315 | fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; | ||
316 | |||
317 | return fe; | ||
318 | } | ||
319 | EXPORT_SYMBOL(ts2020_attach); | ||
320 | |||
321 | MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); | ||
322 | MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); | ||
323 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h new file mode 100644 index 000000000000..13a172dfd582 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | Montage Technology TS2020 - Silicon Tuner driver | ||
3 | Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> | ||
4 | |||
5 | Copyright (C) 2009-2012 TurboSight.com | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; if not, write to the Free Software | ||
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #ifndef TS2020_H | ||
23 | #define TS2020_H | ||
24 | |||
25 | #include <linux/dvb/frontend.h> | ||
26 | |||
27 | struct ts2020_config { | ||
28 | u8 tuner_address; | ||
29 | }; | ||
30 | |||
31 | #if defined(CONFIG_DVB_TS2020) || \ | ||
32 | (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) | ||
33 | |||
34 | extern struct dvb_frontend *ts2020_attach( | ||
35 | struct dvb_frontend *fe, | ||
36 | const struct ts2020_config *config, | ||
37 | struct i2c_adapter *i2c); | ||
38 | #else | ||
39 | static inline struct dvb_frontend *ts2020_attach( | ||
40 | struct dvb_frontend *fe, | ||
41 | const struct ts2020_config *config, | ||
42 | struct i2c_adapter *i2c) | ||
43 | { | ||
44 | printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); | ||
45 | return NULL; | ||
46 | } | ||
47 | #endif | ||
48 | |||
49 | #endif /* TS2020_H */ | ||