diff options
Diffstat (limited to 'drivers/media/dvb/frontends/cx24123.c')
-rw-r--r-- | drivers/media/dvb/frontends/cx24123.c | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c new file mode 100644 index 000000000000..d661c6f9cbe5 --- /dev/null +++ b/drivers/media/dvb/frontends/cx24123.c | |||
@@ -0,0 +1,889 @@ | |||
1 | /* | ||
2 | Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver | ||
3 | |||
4 | Copyright (C) 2005 Steven Toth <stoth@hauppauge.com> | ||
5 | |||
6 | Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc> | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; if not, write to the Free Software | ||
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/slab.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <linux/init.h> | ||
28 | |||
29 | #include "dvb_frontend.h" | ||
30 | #include "cx24123.h" | ||
31 | |||
32 | static int debug; | ||
33 | #define dprintk(args...) \ | ||
34 | do { \ | ||
35 | if (debug) printk (KERN_DEBUG "cx24123: " args); \ | ||
36 | } while (0) | ||
37 | |||
38 | struct cx24123_state | ||
39 | { | ||
40 | struct i2c_adapter* i2c; | ||
41 | struct dvb_frontend_ops ops; | ||
42 | const struct cx24123_config* config; | ||
43 | |||
44 | struct dvb_frontend frontend; | ||
45 | |||
46 | u32 lastber; | ||
47 | u16 snr; | ||
48 | u8 lnbreg; | ||
49 | |||
50 | /* Some PLL specifics for tuning */ | ||
51 | u32 VCAarg; | ||
52 | u32 VGAarg; | ||
53 | u32 bandselectarg; | ||
54 | u32 pllarg; | ||
55 | |||
56 | /* The Demod/Tuner can't easily provide these, we cache them */ | ||
57 | u32 currentfreq; | ||
58 | u32 currentsymbolrate; | ||
59 | }; | ||
60 | |||
61 | /* Various tuner defaults need to be established for a given symbol rate Sps */ | ||
62 | static struct | ||
63 | { | ||
64 | u32 symbolrate_low; | ||
65 | u32 symbolrate_high; | ||
66 | u32 VCAslope; | ||
67 | u32 VCAoffset; | ||
68 | u32 VGA1offset; | ||
69 | u32 VGA2offset; | ||
70 | u32 VCAprogdata; | ||
71 | u32 VGAprogdata; | ||
72 | } cx24123_AGC_vals[] = | ||
73 | { | ||
74 | { | ||
75 | .symbolrate_low = 1000000, | ||
76 | .symbolrate_high = 4999999, | ||
77 | .VCAslope = 0x07, | ||
78 | .VCAoffset = 0x0f, | ||
79 | .VGA1offset = 0x1f8, | ||
80 | .VGA2offset = 0x1f8, | ||
81 | .VGAprogdata = (2 << 18) | (0x1f8 << 9) | 0x1f8, | ||
82 | .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x07, | ||
83 | }, | ||
84 | { | ||
85 | .symbolrate_low = 5000000, | ||
86 | .symbolrate_high = 14999999, | ||
87 | .VCAslope = 0x1f, | ||
88 | .VCAoffset = 0x1f, | ||
89 | .VGA1offset = 0x1e0, | ||
90 | .VGA2offset = 0x180, | ||
91 | .VGAprogdata = (2 << 18) | (0x180 << 9) | 0x1e0, | ||
92 | .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x1f, | ||
93 | }, | ||
94 | { | ||
95 | .symbolrate_low = 15000000, | ||
96 | .symbolrate_high = 45000000, | ||
97 | .VCAslope = 0x3f, | ||
98 | .VCAoffset = 0x3f, | ||
99 | .VGA1offset = 0x180, | ||
100 | .VGA2offset = 0x100, | ||
101 | .VGAprogdata = (2 << 18) | (0x100 << 9) | 0x180, | ||
102 | .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x3f, | ||
103 | }, | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * Various tuner defaults need to be established for a given frequency kHz. | ||
108 | * fixme: The bounds on the bands do not match the doc in real life. | ||
109 | * fixme: Some of them have been moved, other might need adjustment. | ||
110 | */ | ||
111 | static struct | ||
112 | { | ||
113 | u32 freq_low; | ||
114 | u32 freq_high; | ||
115 | u32 bandselect; | ||
116 | u32 VCOdivider; | ||
117 | u32 VCOnumber; | ||
118 | u32 progdata; | ||
119 | } cx24123_bandselect_vals[] = | ||
120 | { | ||
121 | { | ||
122 | .freq_low = 950000, | ||
123 | .freq_high = 1018999, | ||
124 | .bandselect = 0x40, | ||
125 | .VCOdivider = 4, | ||
126 | .VCOnumber = 7, | ||
127 | .progdata = (0 << 18) | (0 << 9) | 0x40, | ||
128 | }, | ||
129 | { | ||
130 | .freq_low = 1019000, | ||
131 | .freq_high = 1074999, | ||
132 | .bandselect = 0x80, | ||
133 | .VCOdivider = 4, | ||
134 | .VCOnumber = 8, | ||
135 | .progdata = (0 << 18) | (0 << 9) | 0x80, | ||
136 | }, | ||
137 | { | ||
138 | .freq_low = 1075000, | ||
139 | .freq_high = 1227999, | ||
140 | .bandselect = 0x01, | ||
141 | .VCOdivider = 2, | ||
142 | .VCOnumber = 1, | ||
143 | .progdata = (0 << 18) | (1 << 9) | 0x01, | ||
144 | }, | ||
145 | { | ||
146 | .freq_low = 1228000, | ||
147 | .freq_high = 1349999, | ||
148 | .bandselect = 0x02, | ||
149 | .VCOdivider = 2, | ||
150 | .VCOnumber = 2, | ||
151 | .progdata = (0 << 18) | (1 << 9) | 0x02, | ||
152 | }, | ||
153 | { | ||
154 | .freq_low = 1350000, | ||
155 | .freq_high = 1481999, | ||
156 | .bandselect = 0x04, | ||
157 | .VCOdivider = 2, | ||
158 | .VCOnumber = 3, | ||
159 | .progdata = (0 << 18) | (1 << 9) | 0x04, | ||
160 | }, | ||
161 | { | ||
162 | .freq_low = 1482000, | ||
163 | .freq_high = 1595999, | ||
164 | .bandselect = 0x08, | ||
165 | .VCOdivider = 2, | ||
166 | .VCOnumber = 4, | ||
167 | .progdata = (0 << 18) | (1 << 9) | 0x08, | ||
168 | }, | ||
169 | { | ||
170 | .freq_low = 1596000, | ||
171 | .freq_high = 1717999, | ||
172 | .bandselect = 0x10, | ||
173 | .VCOdivider = 2, | ||
174 | .VCOnumber = 5, | ||
175 | .progdata = (0 << 18) | (1 << 9) | 0x10, | ||
176 | }, | ||
177 | { | ||
178 | .freq_low = 1718000, | ||
179 | .freq_high = 1855999, | ||
180 | .bandselect = 0x20, | ||
181 | .VCOdivider = 2, | ||
182 | .VCOnumber = 6, | ||
183 | .progdata = (0 << 18) | (1 << 9) | 0x20, | ||
184 | }, | ||
185 | { | ||
186 | .freq_low = 1856000, | ||
187 | .freq_high = 2035999, | ||
188 | .bandselect = 0x40, | ||
189 | .VCOdivider = 2, | ||
190 | .VCOnumber = 7, | ||
191 | .progdata = (0 << 18) | (1 << 9) | 0x40, | ||
192 | }, | ||
193 | { | ||
194 | .freq_low = 2036000, | ||
195 | .freq_high = 2149999, | ||
196 | .bandselect = 0x80, | ||
197 | .VCOdivider = 2, | ||
198 | .VCOnumber = 8, | ||
199 | .progdata = (0 << 18) | (1 << 9) | 0x80, | ||
200 | }, | ||
201 | }; | ||
202 | |||
203 | static struct { | ||
204 | u8 reg; | ||
205 | u8 data; | ||
206 | } cx24123_regdata[] = | ||
207 | { | ||
208 | {0x00, 0x03}, /* Reset system */ | ||
209 | {0x00, 0x00}, /* Clear reset */ | ||
210 | {0x01, 0x3b}, /* Apply sensible defaults, from an i2c sniffer */ | ||
211 | {0x03, 0x07}, | ||
212 | {0x04, 0x10}, | ||
213 | {0x05, 0x04}, | ||
214 | {0x06, 0x31}, | ||
215 | {0x0d, 0x02}, | ||
216 | {0x0e, 0x03}, | ||
217 | {0x0f, 0xfe}, | ||
218 | {0x10, 0x01}, | ||
219 | {0x14, 0x01}, | ||
220 | {0x15, 0x98}, | ||
221 | {0x16, 0x00}, | ||
222 | {0x17, 0x01}, | ||
223 | {0x1b, 0x05}, | ||
224 | {0x1c, 0x80}, | ||
225 | {0x1d, 0x00}, | ||
226 | {0x1e, 0x00}, | ||
227 | {0x20, 0x41}, | ||
228 | {0x21, 0x15}, | ||
229 | {0x27, 0x14}, | ||
230 | {0x28, 0x46}, | ||
231 | {0x29, 0x00}, | ||
232 | {0x2a, 0xb0}, | ||
233 | {0x2b, 0x73}, | ||
234 | {0x2c, 0x00}, | ||
235 | {0x2d, 0x00}, | ||
236 | {0x2e, 0x00}, | ||
237 | {0x2f, 0x00}, | ||
238 | {0x30, 0x00}, | ||
239 | {0x31, 0x00}, | ||
240 | {0x32, 0x8c}, | ||
241 | {0x33, 0x00}, | ||
242 | {0x34, 0x00}, | ||
243 | {0x35, 0x03}, | ||
244 | {0x36, 0x02}, | ||
245 | {0x37, 0x3a}, | ||
246 | {0x3a, 0x00}, /* Enable AGC accumulator */ | ||
247 | {0x44, 0x00}, | ||
248 | {0x45, 0x00}, | ||
249 | {0x46, 0x05}, | ||
250 | {0x56, 0x41}, | ||
251 | {0x57, 0xff}, | ||
252 | {0x67, 0x83}, | ||
253 | }; | ||
254 | |||
255 | static int cx24123_writereg(struct cx24123_state* state, int reg, int data) | ||
256 | { | ||
257 | u8 buf[] = { reg, data }; | ||
258 | struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; | ||
259 | int err; | ||
260 | |||
261 | if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { | ||
262 | printk("%s: writereg error(err == %i, reg == 0x%02x," | ||
263 | " data == 0x%02x)\n", __FUNCTION__, err, reg, data); | ||
264 | return -EREMOTEIO; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int cx24123_writelnbreg(struct cx24123_state* state, int reg, int data) | ||
271 | { | ||
272 | u8 buf[] = { reg, data }; | ||
273 | /* fixme: put the intersil addr int the config */ | ||
274 | struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 2 }; | ||
275 | int err; | ||
276 | |||
277 | if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { | ||
278 | printk("%s: writelnbreg error (err == %i, reg == 0x%02x," | ||
279 | " data == 0x%02x)\n", __FUNCTION__, err, reg, data); | ||
280 | return -EREMOTEIO; | ||
281 | } | ||
282 | |||
283 | /* cache the write, no way to read back */ | ||
284 | state->lnbreg = data; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int cx24123_readreg(struct cx24123_state* state, u8 reg) | ||
290 | { | ||
291 | int ret; | ||
292 | u8 b0[] = { reg }; | ||
293 | u8 b1[] = { 0 }; | ||
294 | struct i2c_msg msg[] = { | ||
295 | { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, | ||
296 | { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } | ||
297 | }; | ||
298 | |||
299 | ret = i2c_transfer(state->i2c, msg, 2); | ||
300 | |||
301 | if (ret != 2) { | ||
302 | printk("%s: reg=0x%x (error=%d)\n", __FUNCTION__, reg, ret); | ||
303 | return ret; | ||
304 | } | ||
305 | |||
306 | return b1[0]; | ||
307 | } | ||
308 | |||
309 | static int cx24123_readlnbreg(struct cx24123_state* state, u8 reg) | ||
310 | { | ||
311 | return state->lnbreg; | ||
312 | } | ||
313 | |||
314 | static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_inversion_t inversion) | ||
315 | { | ||
316 | switch (inversion) { | ||
317 | case INVERSION_OFF: | ||
318 | cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) & 0x7f); | ||
319 | cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); | ||
320 | break; | ||
321 | case INVERSION_ON: | ||
322 | cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) | 0x80); | ||
323 | cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); | ||
324 | break; | ||
325 | case INVERSION_AUTO: | ||
326 | cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) & 0x7f); | ||
327 | break; | ||
328 | default: | ||
329 | return -EINVAL; | ||
330 | } | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int cx24123_get_inversion(struct cx24123_state* state, fe_spectral_inversion_t *inversion) | ||
336 | { | ||
337 | u8 val; | ||
338 | |||
339 | val = cx24123_readreg(state, 0x1b) >> 7; | ||
340 | |||
341 | if (val == 0) | ||
342 | *inversion = INVERSION_OFF; | ||
343 | else | ||
344 | *inversion = INVERSION_ON; | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec) | ||
350 | { | ||
351 | if ( (fec < FEC_NONE) || (fec > FEC_AUTO) ) | ||
352 | fec = FEC_AUTO; | ||
353 | |||
354 | /* Hardware has 5/11 and 3/5 but are never unused */ | ||
355 | switch (fec) { | ||
356 | case FEC_NONE: | ||
357 | return cx24123_writereg(state, 0x0f, 0x01); | ||
358 | case FEC_1_2: | ||
359 | return cx24123_writereg(state, 0x0f, 0x02); | ||
360 | case FEC_2_3: | ||
361 | return cx24123_writereg(state, 0x0f, 0x04); | ||
362 | case FEC_3_4: | ||
363 | return cx24123_writereg(state, 0x0f, 0x08); | ||
364 | case FEC_5_6: | ||
365 | return cx24123_writereg(state, 0x0f, 0x20); | ||
366 | case FEC_7_8: | ||
367 | return cx24123_writereg(state, 0x0f, 0x80); | ||
368 | case FEC_AUTO: | ||
369 | return cx24123_writereg(state, 0x0f, 0xae); | ||
370 | default: | ||
371 | return -EOPNOTSUPP; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec) | ||
376 | { | ||
377 | int ret; | ||
378 | u8 val; | ||
379 | |||
380 | ret = cx24123_readreg (state, 0x1b); | ||
381 | if (ret < 0) | ||
382 | return ret; | ||
383 | val = ret & 0x07; | ||
384 | switch (val) { | ||
385 | case 1: | ||
386 | *fec = FEC_1_2; | ||
387 | break; | ||
388 | case 3: | ||
389 | *fec = FEC_2_3; | ||
390 | break; | ||
391 | case 4: | ||
392 | *fec = FEC_3_4; | ||
393 | break; | ||
394 | case 5: | ||
395 | *fec = FEC_4_5; | ||
396 | break; | ||
397 | case 6: | ||
398 | *fec = FEC_5_6; | ||
399 | break; | ||
400 | case 7: | ||
401 | *fec = FEC_7_8; | ||
402 | break; | ||
403 | case 2: /* *fec = FEC_3_5; break; */ | ||
404 | case 0: /* *fec = FEC_5_11; break; */ | ||
405 | *fec = FEC_AUTO; | ||
406 | break; | ||
407 | default: | ||
408 | *fec = FEC_NONE; // can't happen | ||
409 | } | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | /* fixme: Symbol rates < 3MSps may not work because of precision loss */ | ||
415 | static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate) | ||
416 | { | ||
417 | u32 val; | ||
418 | |||
419 | val = (srate / 1185) * 100; | ||
420 | |||
421 | /* Compensate for scaling up, by removing 17 symbols per 1Msps */ | ||
422 | val = val - (17 * (srate / 1000000)); | ||
423 | |||
424 | cx24123_writereg(state, 0x08, (val >> 16) & 0xff ); | ||
425 | cx24123_writereg(state, 0x09, (val >> 8) & 0xff ); | ||
426 | cx24123_writereg(state, 0x0a, (val ) & 0xff ); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | /* | ||
432 | * Based on the required frequency and symbolrate, the tuner AGC has to be configured | ||
433 | * and the correct band selected. Calculate those values | ||
434 | */ | ||
435 | static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) | ||
436 | { | ||
437 | struct cx24123_state *state = fe->demodulator_priv; | ||
438 | u32 ndiv = 0, adiv = 0, vco_div = 0; | ||
439 | int i = 0; | ||
440 | |||
441 | /* Defaults for low freq, low rate */ | ||
442 | state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; | ||
443 | state->VGAarg = cx24123_AGC_vals[0].VGAprogdata; | ||
444 | state->bandselectarg = cx24123_bandselect_vals[0].progdata; | ||
445 | vco_div = cx24123_bandselect_vals[0].VCOdivider; | ||
446 | |||
447 | /* For the given symbolerate, determine the VCA and VGA programming bits */ | ||
448 | for (i = 0; i < sizeof(cx24123_AGC_vals) / sizeof(cx24123_AGC_vals[0]); i++) | ||
449 | { | ||
450 | if ((cx24123_AGC_vals[i].symbolrate_low <= p->u.qpsk.symbol_rate) && | ||
451 | (cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) { | ||
452 | state->VCAarg = cx24123_AGC_vals[i].VCAprogdata; | ||
453 | state->VGAarg = cx24123_AGC_vals[i].VGAprogdata; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | /* For the given frequency, determine the bandselect programming bits */ | ||
458 | for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++) | ||
459 | { | ||
460 | if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) && | ||
461 | (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) { | ||
462 | state->bandselectarg = cx24123_bandselect_vals[i].progdata; | ||
463 | vco_div = cx24123_bandselect_vals[i].VCOdivider; | ||
464 | } | ||
465 | } | ||
466 | |||
467 | /* Determine the N/A dividers for the requested lband freq (in kHz). */ | ||
468 | /* Note: 10111 (kHz) is the Crystal Freq and divider of 10. */ | ||
469 | ndiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) / 32) & 0x1ff; | ||
470 | adiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) % 32) & 0x1f; | ||
471 | |||
472 | if (adiv == 0) | ||
473 | adiv++; | ||
474 | |||
475 | /* determine the correct pll frequency values. */ | ||
476 | /* Command 11, refdiv 11, cpump polarity 1, cpump current 3mA 10. */ | ||
477 | state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (2 << 14); | ||
478 | state->pllarg |= (ndiv << 5) | adiv; | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * Tuner data is 21 bits long, must be left-aligned in data. | ||
485 | * Tuner cx24109 is written through a dedicated 3wire interface on the demod chip. | ||
486 | */ | ||
487 | static int cx24123_pll_writereg(struct dvb_frontend* fe, struct dvb_frontend_parameters *p, u32 data) | ||
488 | { | ||
489 | struct cx24123_state *state = fe->demodulator_priv; | ||
490 | unsigned long timeout; | ||
491 | |||
492 | /* align the 21 bytes into to bit23 boundary */ | ||
493 | data = data << 3; | ||
494 | |||
495 | /* Reset the demod pll word length to 0x15 bits */ | ||
496 | cx24123_writereg(state, 0x21, 0x15); | ||
497 | |||
498 | /* write the msb 8 bits, wait for the send to be completed */ | ||
499 | timeout = jiffies + msecs_to_jiffies(40); | ||
500 | cx24123_writereg(state, 0x22, (data >> 16) & 0xff); | ||
501 | while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { | ||
502 | if (time_after(jiffies, timeout)) { | ||
503 | printk("%s: demodulator is not responding, possibly hung, aborting.\n", __FUNCTION__); | ||
504 | return -EREMOTEIO; | ||
505 | } | ||
506 | msleep(10); | ||
507 | } | ||
508 | |||
509 | /* send another 8 bytes, wait for the send to be completed */ | ||
510 | timeout = jiffies + msecs_to_jiffies(40); | ||
511 | cx24123_writereg(state, 0x22, (data>>8) & 0xff ); | ||
512 | while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { | ||
513 | if (time_after(jiffies, timeout)) { | ||
514 | printk("%s: demodulator is not responding, possibly hung, aborting.\n", __FUNCTION__); | ||
515 | return -EREMOTEIO; | ||
516 | } | ||
517 | msleep(10); | ||
518 | } | ||
519 | |||
520 | /* send the lower 5 bits of this byte, padded with 3 LBB, wait for the send to be completed */ | ||
521 | timeout = jiffies + msecs_to_jiffies(40); | ||
522 | cx24123_writereg(state, 0x22, (data) & 0xff ); | ||
523 | while ((cx24123_readreg(state, 0x20) & 0x80)) { | ||
524 | if (time_after(jiffies, timeout)) { | ||
525 | printk("%s: demodulator is not responding, possibly hung, aborting.\n", __FUNCTION__); | ||
526 | return -EREMOTEIO; | ||
527 | } | ||
528 | msleep(10); | ||
529 | } | ||
530 | |||
531 | /* Trigger the demod to configure the tuner */ | ||
532 | cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2); | ||
533 | cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd); | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int cx24123_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) | ||
539 | { | ||
540 | struct cx24123_state *state = fe->demodulator_priv; | ||
541 | |||
542 | if (cx24123_pll_calculate(fe, p) != 0) { | ||
543 | printk("%s: cx24123_pll_calcutate failed\n",__FUNCTION__); | ||
544 | return -EINVAL; | ||
545 | } | ||
546 | |||
547 | /* Write the new VCO/VGA */ | ||
548 | cx24123_pll_writereg(fe, p, state->VCAarg); | ||
549 | cx24123_pll_writereg(fe, p, state->VGAarg); | ||
550 | |||
551 | /* Write the new bandselect and pll args */ | ||
552 | cx24123_pll_writereg(fe, p, state->bandselectarg); | ||
553 | cx24123_pll_writereg(fe, p, state->pllarg); | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int cx24123_initfe(struct dvb_frontend* fe) | ||
559 | { | ||
560 | struct cx24123_state *state = fe->demodulator_priv; | ||
561 | int i; | ||
562 | |||
563 | /* Configure the demod to a good set of defaults */ | ||
564 | for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) | ||
565 | cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); | ||
566 | |||
567 | if (state->config->pll_init) | ||
568 | state->config->pll_init(fe); | ||
569 | |||
570 | /* Configure the LNB for 14V */ | ||
571 | if (state->config->use_isl6421) | ||
572 | cx24123_writelnbreg(state, 0x0, 0x2a); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) | ||
578 | { | ||
579 | struct cx24123_state *state = fe->demodulator_priv; | ||
580 | u8 val; | ||
581 | |||
582 | switch (state->config->use_isl6421) { | ||
583 | |||
584 | case 1: | ||
585 | |||
586 | val = cx24123_readlnbreg(state, 0x0); | ||
587 | |||
588 | switch (voltage) { | ||
589 | case SEC_VOLTAGE_13: | ||
590 | return cx24123_writelnbreg(state, 0x0, val & 0x32); /* V 13v */ | ||
591 | case SEC_VOLTAGE_18: | ||
592 | return cx24123_writelnbreg(state, 0x0, val | 0x04); /* H 18v */ | ||
593 | case SEC_VOLTAGE_OFF: | ||
594 | return cx24123_writelnbreg(state, 0x0, val & 0x30); | ||
595 | default: | ||
596 | return -EINVAL; | ||
597 | }; | ||
598 | |||
599 | case 0: | ||
600 | |||
601 | val = cx24123_readreg(state, 0x29); | ||
602 | |||
603 | switch (voltage) { | ||
604 | case SEC_VOLTAGE_13: | ||
605 | dprintk("%s: setting voltage 13V\n", __FUNCTION__); | ||
606 | if (state->config->enable_lnb_voltage) | ||
607 | state->config->enable_lnb_voltage(fe, 1); | ||
608 | return cx24123_writereg(state, 0x29, val | 0x80); | ||
609 | case SEC_VOLTAGE_18: | ||
610 | dprintk("%s: setting voltage 18V\n", __FUNCTION__); | ||
611 | if (state->config->enable_lnb_voltage) | ||
612 | state->config->enable_lnb_voltage(fe, 1); | ||
613 | return cx24123_writereg(state, 0x29, val & 0x7f); | ||
614 | case SEC_VOLTAGE_OFF: | ||
615 | dprintk("%s: setting voltage off\n", __FUNCTION__); | ||
616 | if (state->config->enable_lnb_voltage) | ||
617 | state->config->enable_lnb_voltage(fe, 0); | ||
618 | return 0; | ||
619 | default: | ||
620 | return -EINVAL; | ||
621 | }; | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, | ||
628 | struct dvb_diseqc_master_cmd *cmd) | ||
629 | { | ||
630 | /* fixme: Implement diseqc */ | ||
631 | printk("%s: No support yet\n",__FUNCTION__); | ||
632 | |||
633 | return -ENOTSUPP; | ||
634 | } | ||
635 | |||
636 | static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status) | ||
637 | { | ||
638 | struct cx24123_state *state = fe->demodulator_priv; | ||
639 | |||
640 | int sync = cx24123_readreg(state, 0x14); | ||
641 | int lock = cx24123_readreg(state, 0x20); | ||
642 | |||
643 | *status = 0; | ||
644 | if (lock & 0x01) | ||
645 | *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; | ||
646 | if (sync & 0x04) | ||
647 | *status |= FE_HAS_VITERBI; | ||
648 | if (sync & 0x08) | ||
649 | *status |= FE_HAS_CARRIER; | ||
650 | if (sync & 0x80) | ||
651 | *status |= FE_HAS_SYNC | FE_HAS_LOCK; | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | /* | ||
657 | * Configured to return the measurement of errors in blocks, because no UCBLOCKS value | ||
658 | * is available, so this value doubles up to satisfy both measurements | ||
659 | */ | ||
660 | static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber) | ||
661 | { | ||
662 | struct cx24123_state *state = fe->demodulator_priv; | ||
663 | |||
664 | state->lastber = | ||
665 | ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | | ||
666 | (cx24123_readreg(state, 0x1d) << 8 | | ||
667 | cx24123_readreg(state, 0x1e)); | ||
668 | |||
669 | /* Do the signal quality processing here, it's derived from the BER. */ | ||
670 | /* Scale the BER from a 24bit to a SNR 16 bit where higher = better */ | ||
671 | if (state->lastber < 5000) | ||
672 | state->snr = 655*100; | ||
673 | else if ( (state->lastber >= 5000) && (state->lastber < 55000) ) | ||
674 | state->snr = 655*90; | ||
675 | else if ( (state->lastber >= 55000) && (state->lastber < 150000) ) | ||
676 | state->snr = 655*80; | ||
677 | else if ( (state->lastber >= 150000) && (state->lastber < 250000) ) | ||
678 | state->snr = 655*70; | ||
679 | else if ( (state->lastber >= 250000) && (state->lastber < 450000) ) | ||
680 | state->snr = 655*65; | ||
681 | else | ||
682 | state->snr = 0; | ||
683 | |||
684 | *ber = state->lastber; | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) | ||
690 | { | ||
691 | struct cx24123_state *state = fe->demodulator_priv; | ||
692 | *signal_strength = cx24123_readreg(state, 0x3b) << 8; /* larger = better */ | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr) | ||
698 | { | ||
699 | struct cx24123_state *state = fe->demodulator_priv; | ||
700 | *snr = state->snr; | ||
701 | |||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) | ||
706 | { | ||
707 | struct cx24123_state *state = fe->demodulator_priv; | ||
708 | *ucblocks = state->lastber; | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int cx24123_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) | ||
714 | { | ||
715 | struct cx24123_state *state = fe->demodulator_priv; | ||
716 | |||
717 | if (state->config->set_ts_params) | ||
718 | state->config->set_ts_params(fe, 0); | ||
719 | |||
720 | state->currentfreq=p->frequency; | ||
721 | state->currentsymbolrate = p->u.qpsk.symbol_rate; | ||
722 | |||
723 | cx24123_set_inversion(state, p->inversion); | ||
724 | cx24123_set_fec(state, p->u.qpsk.fec_inner); | ||
725 | cx24123_set_symbolrate(state, p->u.qpsk.symbol_rate); | ||
726 | cx24123_pll_tune(fe, p); | ||
727 | |||
728 | /* Enable automatic aquisition and reset cycle */ | ||
729 | cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07)); | ||
730 | cx24123_writereg(state, 0x00, 0x10); | ||
731 | cx24123_writereg(state, 0x00, 0); | ||
732 | |||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int cx24123_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) | ||
737 | { | ||
738 | struct cx24123_state *state = fe->demodulator_priv; | ||
739 | |||
740 | if (cx24123_get_inversion(state, &p->inversion) != 0) { | ||
741 | printk("%s: Failed to get inversion status\n",__FUNCTION__); | ||
742 | return -EREMOTEIO; | ||
743 | } | ||
744 | if (cx24123_get_fec(state, &p->u.qpsk.fec_inner) != 0) { | ||
745 | printk("%s: Failed to get fec status\n",__FUNCTION__); | ||
746 | return -EREMOTEIO; | ||
747 | } | ||
748 | p->frequency = state->currentfreq; | ||
749 | p->u.qpsk.symbol_rate = state->currentsymbolrate; | ||
750 | |||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) | ||
755 | { | ||
756 | struct cx24123_state *state = fe->demodulator_priv; | ||
757 | u8 val; | ||
758 | |||
759 | switch (state->config->use_isl6421) { | ||
760 | case 1: | ||
761 | |||
762 | val = cx24123_readlnbreg(state, 0x0); | ||
763 | |||
764 | switch (tone) { | ||
765 | case SEC_TONE_ON: | ||
766 | return cx24123_writelnbreg(state, 0x0, val | 0x10); | ||
767 | case SEC_TONE_OFF: | ||
768 | return cx24123_writelnbreg(state, 0x0, val & 0x2f); | ||
769 | default: | ||
770 | printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); | ||
771 | return -EINVAL; | ||
772 | } | ||
773 | |||
774 | case 0: | ||
775 | |||
776 | val = cx24123_readreg(state, 0x29); | ||
777 | |||
778 | switch (tone) { | ||
779 | case SEC_TONE_ON: | ||
780 | dprintk("%s: setting tone on\n", __FUNCTION__); | ||
781 | return cx24123_writereg(state, 0x29, val | 0x10); | ||
782 | case SEC_TONE_OFF: | ||
783 | dprintk("%s: setting tone off\n",__FUNCTION__); | ||
784 | return cx24123_writereg(state, 0x29, val & 0xef); | ||
785 | default: | ||
786 | printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); | ||
787 | return -EINVAL; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | static void cx24123_release(struct dvb_frontend* fe) | ||
795 | { | ||
796 | struct cx24123_state* state = fe->demodulator_priv; | ||
797 | dprintk("%s\n",__FUNCTION__); | ||
798 | kfree(state); | ||
799 | } | ||
800 | |||
801 | static struct dvb_frontend_ops cx24123_ops; | ||
802 | |||
803 | struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, | ||
804 | struct i2c_adapter* i2c) | ||
805 | { | ||
806 | struct cx24123_state* state = NULL; | ||
807 | int ret; | ||
808 | |||
809 | dprintk("%s\n",__FUNCTION__); | ||
810 | |||
811 | /* allocate memory for the internal state */ | ||
812 | state = kmalloc(sizeof(struct cx24123_state), GFP_KERNEL); | ||
813 | if (state == NULL) { | ||
814 | printk("Unable to kmalloc\n"); | ||
815 | goto error; | ||
816 | } | ||
817 | |||
818 | /* setup the state */ | ||
819 | state->config = config; | ||
820 | state->i2c = i2c; | ||
821 | memcpy(&state->ops, &cx24123_ops, sizeof(struct dvb_frontend_ops)); | ||
822 | state->lastber = 0; | ||
823 | state->snr = 0; | ||
824 | state->lnbreg = 0; | ||
825 | state->VCAarg = 0; | ||
826 | state->VGAarg = 0; | ||
827 | state->bandselectarg = 0; | ||
828 | state->pllarg = 0; | ||
829 | state->currentfreq = 0; | ||
830 | state->currentsymbolrate = 0; | ||
831 | |||
832 | /* check if the demod is there */ | ||
833 | ret = cx24123_readreg(state, 0x00); | ||
834 | if ((ret != 0xd1) && (ret != 0xe1)) { | ||
835 | printk("Version != d1 or e1\n"); | ||
836 | goto error; | ||
837 | } | ||
838 | |||
839 | /* create dvb_frontend */ | ||
840 | state->frontend.ops = &state->ops; | ||
841 | state->frontend.demodulator_priv = state; | ||
842 | return &state->frontend; | ||
843 | |||
844 | error: | ||
845 | kfree(state); | ||
846 | |||
847 | return NULL; | ||
848 | } | ||
849 | |||
850 | static struct dvb_frontend_ops cx24123_ops = { | ||
851 | |||
852 | .info = { | ||
853 | .name = "Conexant CX24123/CX24109", | ||
854 | .type = FE_QPSK, | ||
855 | .frequency_min = 950000, | ||
856 | .frequency_max = 2150000, | ||
857 | .frequency_stepsize = 1011, /* kHz for QPSK frontends */ | ||
858 | .frequency_tolerance = 29500, | ||
859 | .symbol_rate_min = 1000000, | ||
860 | .symbol_rate_max = 45000000, | ||
861 | .caps = FE_CAN_INVERSION_AUTO | | ||
862 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | ||
863 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | ||
864 | FE_CAN_QPSK | FE_CAN_RECOVER | ||
865 | }, | ||
866 | |||
867 | .release = cx24123_release, | ||
868 | |||
869 | .init = cx24123_initfe, | ||
870 | .set_frontend = cx24123_set_frontend, | ||
871 | .get_frontend = cx24123_get_frontend, | ||
872 | .read_status = cx24123_read_status, | ||
873 | .read_ber = cx24123_read_ber, | ||
874 | .read_signal_strength = cx24123_read_signal_strength, | ||
875 | .read_snr = cx24123_read_snr, | ||
876 | .read_ucblocks = cx24123_read_ucblocks, | ||
877 | .diseqc_send_master_cmd = cx24123_send_diseqc_msg, | ||
878 | .set_tone = cx24123_set_tone, | ||
879 | .set_voltage = cx24123_set_voltage, | ||
880 | }; | ||
881 | |||
882 | module_param(debug, int, 0644); | ||
883 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | ||
884 | |||
885 | MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24123/cx24109 hardware"); | ||
886 | MODULE_AUTHOR("Steven Toth"); | ||
887 | MODULE_LICENSE("GPL"); | ||
888 | |||
889 | EXPORT_SYMBOL(cx24123_attach); | ||