diff options
author | Patrick Boettcher <pb@linuxtv.org> | 2008-11-23 14:26:54 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:38:34 -0500 |
commit | 4c48ae8e8a490f16abbd874602135bcd83c84f26 (patch) | |
tree | bfc24684580dc6ddffb462092b288917ba424f69 /drivers/media/dvb/frontends/cx24113.c | |
parent | 2bdd29cf3d4d32e4371fbd6b27ea171f2c1f0836 (diff) |
V4L/DVB (9811): Add support for the CX24113 DVB-S tuner driver
This commit adds support for the CX24113 DVB-S tuner driver and thus support for the Technisat Skystar2 revision 2.8.
The driver was created with the help of Technisat. Thank you very much.
Signed-off-by: Patrick Boettcher <pb@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb/frontends/cx24113.c')
-rw-r--r-- | drivers/media/dvb/frontends/cx24113.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c new file mode 100644 index 000000000000..e2e5df9de1ec --- /dev/null +++ b/drivers/media/dvb/frontends/cx24113.c | |||
@@ -0,0 +1,608 @@ | |||
1 | /* | ||
2 | * Driver for Conexant CX24113/CX24128 Tuner (Satelite) | ||
3 | * | ||
4 | * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org> | ||
5 | * | ||
6 | * Developed for BBTI / Technisat | ||
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 | * | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/slab.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | |||
29 | #include "dvb_frontend.h" | ||
30 | #include "cx24113.h" | ||
31 | |||
32 | static int debug; | ||
33 | |||
34 | #define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) | ||
35 | #define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) | ||
36 | |||
37 | #define dprintk(args...) \ | ||
38 | do { \ | ||
39 | if (debug) { \ | ||
40 | printk(KERN_DEBUG "CX24113: %s: ", __func__); \ | ||
41 | printk(args); \ | ||
42 | } \ | ||
43 | } while (0) | ||
44 | |||
45 | struct cx24113_state { | ||
46 | struct i2c_adapter *i2c; | ||
47 | const struct cx24113_config *config; | ||
48 | |||
49 | #define REV_CX24113 0x23 | ||
50 | u8 rev; | ||
51 | u8 ver; | ||
52 | |||
53 | u8 icp_mode:1; | ||
54 | |||
55 | #define ICP_LEVEL1 0 | ||
56 | #define ICP_LEVEL2 1 | ||
57 | #define ICP_LEVEL3 2 | ||
58 | #define ICP_LEVEL4 3 | ||
59 | u8 icp_man:2; | ||
60 | u8 icp_auto_low:2; | ||
61 | u8 icp_auto_mlow:2; | ||
62 | u8 icp_auto_mhi:2; | ||
63 | u8 icp_auto_hi:2; | ||
64 | u8 icp_dig; | ||
65 | |||
66 | #define LNA_MIN_GAIN 0 | ||
67 | #define LNA_MID_GAIN 1 | ||
68 | #define LNA_MAX_GAIN 2 | ||
69 | u8 lna_gain:2; | ||
70 | |||
71 | u8 acp_on:1; | ||
72 | |||
73 | u8 vco_mode:2; | ||
74 | u8 vco_shift:1; | ||
75 | #define VCOBANDSEL_6 0x80 | ||
76 | #define VCOBANDSEL_5 0x01 | ||
77 | #define VCOBANDSEL_4 0x02 | ||
78 | #define VCOBANDSEL_3 0x04 | ||
79 | #define VCOBANDSEL_2 0x08 | ||
80 | #define VCOBANDSEL_1 0x10 | ||
81 | u8 vco_band; | ||
82 | |||
83 | #define VCODIV4 4 | ||
84 | #define VCODIV2 2 | ||
85 | u8 vcodiv; | ||
86 | |||
87 | u8 bs_delay:4; | ||
88 | u16 bs_freqcnt:13; | ||
89 | u16 bs_rdiv; | ||
90 | u8 prescaler_mode:1; | ||
91 | |||
92 | u8 rfvga_bias_ctrl; | ||
93 | |||
94 | s16 tuner_gain_thres; | ||
95 | u8 gain_level; | ||
96 | |||
97 | u32 frequency; | ||
98 | |||
99 | u8 refdiv; | ||
100 | |||
101 | u8 Fwindow_enabled; | ||
102 | }; | ||
103 | |||
104 | static int cx24113_writereg(struct cx24113_state *state, int reg, int data) | ||
105 | { | ||
106 | u8 buf[] = { reg, data }; | ||
107 | struct i2c_msg msg = { .addr = state->config->i2c_addr, | ||
108 | .flags = 0, .buf = buf, .len = 2 }; | ||
109 | int err = i2c_transfer(state->i2c, &msg, 1); | ||
110 | if (err != 1) { | ||
111 | printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x," | ||
112 | " data == 0x%02x)\n", __func__, err, reg, data); | ||
113 | return err; | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int cx24113_readreg(struct cx24113_state *state, u8 reg) | ||
120 | { | ||
121 | int ret; | ||
122 | u8 b; | ||
123 | struct i2c_msg msg[] = { | ||
124 | { .addr = state->config->i2c_addr, | ||
125 | .flags = 0, .buf = ®, .len = 1 }, | ||
126 | { .addr = state->config->i2c_addr, | ||
127 | .flags = I2C_M_RD, .buf = &b, .len = 1 } | ||
128 | }; | ||
129 | |||
130 | ret = i2c_transfer(state->i2c, msg, 2); | ||
131 | |||
132 | if (ret != 2) { | ||
133 | printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", | ||
134 | __func__, reg, ret); | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | return b; | ||
139 | } | ||
140 | |||
141 | static void cx24113_set_parameters(struct cx24113_state *state) | ||
142 | { | ||
143 | u8 r; | ||
144 | |||
145 | r = cx24113_readreg(state, 0x10) & 0x82; | ||
146 | r |= state->icp_mode; | ||
147 | r |= state->icp_man << 4; | ||
148 | r |= state->icp_dig << 2; | ||
149 | r |= state->prescaler_mode << 5; | ||
150 | cx24113_writereg(state, 0x10, r); | ||
151 | |||
152 | r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) | ||
153 | | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); | ||
154 | cx24113_writereg(state, 0x11, r); | ||
155 | |||
156 | if (state->rev == REV_CX24113) { | ||
157 | r = cx24113_readreg(state, 0x20) & 0xec; | ||
158 | r |= state->lna_gain; | ||
159 | r |= state->rfvga_bias_ctrl << 4; | ||
160 | cx24113_writereg(state, 0x20, r); | ||
161 | } | ||
162 | |||
163 | r = cx24113_readreg(state, 0x12) & 0x03; | ||
164 | r |= state->acp_on << 2; | ||
165 | r |= state->bs_delay << 4; | ||
166 | cx24113_writereg(state, 0x12, r); | ||
167 | |||
168 | r = cx24113_readreg(state, 0x18) & 0x40; | ||
169 | r |= state->vco_shift; | ||
170 | if (state->vco_band == VCOBANDSEL_6) | ||
171 | r |= (1 << 7); | ||
172 | else | ||
173 | r |= (state->vco_band << 1); | ||
174 | cx24113_writereg(state, 0x18, r); | ||
175 | |||
176 | r = cx24113_readreg(state, 0x14) & 0x20; | ||
177 | r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); | ||
178 | cx24113_writereg(state, 0x14, r); | ||
179 | cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); | ||
180 | |||
181 | cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); | ||
182 | r = (cx24113_readreg(state, 0x17) & 0x0f) | | ||
183 | ((state->bs_rdiv & 0x0f) << 4); | ||
184 | cx24113_writereg(state, 0x17, r); | ||
185 | } | ||
186 | |||
187 | #define VGA_0 0x00 | ||
188 | #define VGA_1 0x04 | ||
189 | #define VGA_2 0x02 | ||
190 | #define VGA_3 0x06 | ||
191 | #define VGA_4 0x01 | ||
192 | #define VGA_5 0x05 | ||
193 | #define VGA_6 0x03 | ||
194 | #define VGA_7 0x07 | ||
195 | |||
196 | #define RFVGA_0 0x00 | ||
197 | #define RFVGA_1 0x01 | ||
198 | #define RFVGA_2 0x02 | ||
199 | #define RFVGA_3 0x03 | ||
200 | |||
201 | static int cx24113_set_gain_settings(struct cx24113_state *state, | ||
202 | s16 power_estimation) | ||
203 | { | ||
204 | u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, | ||
205 | vga = cx24113_readreg(state, 0x1f) & 0x3f, | ||
206 | rfvga = cx24113_readreg(state, 0x20) & 0xf3; | ||
207 | u8 gain_level = power_estimation >= state->tuner_gain_thres; | ||
208 | |||
209 | dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", | ||
210 | power_estimation, state->tuner_gain_thres, | ||
211 | state->gain_level, gain_level); | ||
212 | |||
213 | if (gain_level == state->gain_level) | ||
214 | return 0; /* nothing to be done */ | ||
215 | |||
216 | ampout |= 0xf; | ||
217 | |||
218 | if (gain_level) { | ||
219 | rfvga |= RFVGA_0 << 2; | ||
220 | vga |= (VGA_7 << 3) | VGA_7; | ||
221 | } else { | ||
222 | rfvga |= RFVGA_2 << 2; | ||
223 | vga |= (VGA_6 << 3) | VGA_2; | ||
224 | } | ||
225 | state->gain_level = gain_level; | ||
226 | |||
227 | cx24113_writereg(state, 0x1d, ampout); | ||
228 | cx24113_writereg(state, 0x1f, vga); | ||
229 | cx24113_writereg(state, 0x20, rfvga); | ||
230 | |||
231 | return 1; /* did something */ | ||
232 | } | ||
233 | |||
234 | static int cx24113_set_Fref(struct cx24113_state *state, u8 high) | ||
235 | { | ||
236 | u8 xtal = cx24113_readreg(state, 0x02); | ||
237 | if (state->rev == 0x43 && state->vcodiv == VCODIV4) | ||
238 | high = 1; | ||
239 | |||
240 | xtal &= ~0x2; | ||
241 | if (high) | ||
242 | xtal |= high << 1; | ||
243 | return cx24113_writereg(state, 0x02, xtal); | ||
244 | } | ||
245 | |||
246 | static int cx24113_enable(struct cx24113_state *state, u8 enable) | ||
247 | { | ||
248 | u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; | ||
249 | if (state->rev == REV_CX24113) | ||
250 | r21 |= (1 << 1); | ||
251 | return cx24113_writereg(state, 0x21, r21); | ||
252 | } | ||
253 | |||
254 | static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) | ||
255 | { | ||
256 | u8 r; | ||
257 | |||
258 | if (bandwidth_khz <= 19000) | ||
259 | r = 0x03 << 6; | ||
260 | else if (bandwidth_khz <= 25000) | ||
261 | r = 0x02 << 6; | ||
262 | else | ||
263 | r = 0x01 << 6; | ||
264 | |||
265 | dprintk("bandwidth to be set: %d\n", bandwidth_khz); | ||
266 | bandwidth_khz *= 10; | ||
267 | bandwidth_khz -= 10000; | ||
268 | bandwidth_khz /= 1000; | ||
269 | bandwidth_khz += 5; | ||
270 | bandwidth_khz /= 10; | ||
271 | |||
272 | dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); | ||
273 | |||
274 | r |= bandwidth_khz & 0x3f; | ||
275 | |||
276 | return cx24113_writereg(state, 0x1e, r); | ||
277 | } | ||
278 | |||
279 | static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) | ||
280 | { | ||
281 | u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); | ||
282 | return cx24113_writereg(state, 0x10, r); | ||
283 | } | ||
284 | |||
285 | static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) | ||
286 | { | ||
287 | struct cx24113_state *state = fe->tuner_priv; | ||
288 | u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; | ||
289 | if (r) | ||
290 | *status |= TUNER_STATUS_LOCKED; | ||
291 | dprintk("PLL locked: %d\n", r); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) | ||
296 | { | ||
297 | if (state->rev == 0x43 && state->vcodiv == VCODIV4) | ||
298 | refdiv = 2; | ||
299 | return state->refdiv = refdiv; | ||
300 | } | ||
301 | |||
302 | static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) | ||
303 | { | ||
304 | s32 N; | ||
305 | s64 F; | ||
306 | u8 R, r; | ||
307 | u8 vcodiv; | ||
308 | u8 factor; | ||
309 | s32 freq_hz = state->frequency * 1000; | ||
310 | |||
311 | if (state->config->xtal_khz < 20000) | ||
312 | factor = 1; | ||
313 | else | ||
314 | factor = 2; | ||
315 | |||
316 | if (state->rev == REV_CX24113) { | ||
317 | if (state->frequency >= 1100000) | ||
318 | vcodiv = VCODIV2; | ||
319 | else | ||
320 | vcodiv = VCODIV4; | ||
321 | } else { | ||
322 | if (state->frequency >= 1165000) | ||
323 | vcodiv = VCODIV2; | ||
324 | else | ||
325 | vcodiv = VCODIV4; | ||
326 | } | ||
327 | state->vcodiv = vcodiv; | ||
328 | |||
329 | dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); | ||
330 | R = 0; | ||
331 | do { | ||
332 | R = cx24113_set_ref_div(state, R + 1); | ||
333 | |||
334 | /* calculate tuner PLL settings: */ | ||
335 | N = (freq_hz / 100 * vcodiv) * R; | ||
336 | N /= (state->config->xtal_khz) * factor * 2; | ||
337 | N += 5; /* For round up. */ | ||
338 | N /= 10; | ||
339 | N -= 32; | ||
340 | } while (N < 6 && R < 3); | ||
341 | |||
342 | if (N < 6) { | ||
343 | err("strange frequency: N < 6\n"); | ||
344 | return; | ||
345 | } | ||
346 | F = freq_hz; | ||
347 | F *= (u64) (R * vcodiv * 262144); | ||
348 | dprintk("1 N: %d, F: %lld, R: %d\n", N, F, R); | ||
349 | do_div(F, state->config->xtal_khz*1000 * factor * 2); | ||
350 | dprintk("2 N: %d, F: %lld, R: %d\n", N, F, R); | ||
351 | F -= (N + 32) * 262144; | ||
352 | |||
353 | dprintk("3 N: %d, F: %lld, R: %d\n", N, F, R); | ||
354 | |||
355 | if (state->Fwindow_enabled) { | ||
356 | if (F > (262144 / 2 - 1638)) | ||
357 | F = 262144 / 2 - 1638; | ||
358 | if (F < (-262144 / 2 + 1638)) | ||
359 | F = -262144 / 2 + 1638; | ||
360 | if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { | ||
361 | F = 0; | ||
362 | r = cx24113_readreg(state, 0x10); | ||
363 | cx24113_writereg(state, 0x10, r | (1 << 6)); | ||
364 | } | ||
365 | } | ||
366 | dprintk("4 N: %d, F: %lld, R: %d\n", N, F, R); | ||
367 | |||
368 | *n = (u16) N; | ||
369 | *f = (s32) F; | ||
370 | } | ||
371 | |||
372 | |||
373 | static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) | ||
374 | { | ||
375 | u8 reg; | ||
376 | cx24113_writereg(state, 0x19, (n >> 1) & 0xff); | ||
377 | |||
378 | reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); | ||
379 | cx24113_writereg(state, 0x1a, reg); | ||
380 | |||
381 | cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); | ||
382 | |||
383 | reg = cx24113_readreg(state, 0x1c) & 0x1f; | ||
384 | cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); | ||
385 | |||
386 | cx24113_set_Fref(state, r - 1); | ||
387 | } | ||
388 | |||
389 | static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) | ||
390 | { | ||
391 | u8 r = 1; /* or 2 */ | ||
392 | u16 n = 6; | ||
393 | s32 f = 0; | ||
394 | |||
395 | r = cx24113_readreg(state, 0x14); | ||
396 | cx24113_writereg(state, 0x14, r & 0x3f); | ||
397 | |||
398 | r = cx24113_readreg(state, 0x10); | ||
399 | cx24113_writereg(state, 0x10, r & 0xbf); | ||
400 | |||
401 | state->frequency = frequency; | ||
402 | |||
403 | dprintk("tuning to frequency: %d\n", frequency); | ||
404 | |||
405 | cx24113_calc_pll_nf(state, &n, &f); | ||
406 | cx24113_set_nfr(state, n, f, state->refdiv); | ||
407 | |||
408 | r = cx24113_readreg(state, 0x18) & 0xbf; | ||
409 | if (state->vcodiv != VCODIV2) | ||
410 | r |= 1 << 6; | ||
411 | cx24113_writereg(state, 0x18, r); | ||
412 | |||
413 | /* The need for this sleep is not clear. But helps in some cases */ | ||
414 | msleep(5); | ||
415 | |||
416 | r = cx24113_readreg(state, 0x1c) & 0xef; | ||
417 | cx24113_writereg(state, 0x1c, r | (1 << 4)); | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int cx24113_init(struct dvb_frontend *fe) | ||
422 | { | ||
423 | struct cx24113_state *state = fe->tuner_priv; | ||
424 | int ret; | ||
425 | |||
426 | state->tuner_gain_thres = -50; | ||
427 | state->gain_level = 255; /* to force a gain-setting initialization */ | ||
428 | state->icp_mode = 0; | ||
429 | |||
430 | if (state->config->xtal_khz < 11000) { | ||
431 | state->icp_auto_hi = ICP_LEVEL4; | ||
432 | state->icp_auto_mhi = ICP_LEVEL4; | ||
433 | state->icp_auto_mlow = ICP_LEVEL3; | ||
434 | state->icp_auto_low = ICP_LEVEL3; | ||
435 | } else { | ||
436 | state->icp_auto_hi = ICP_LEVEL4; | ||
437 | state->icp_auto_mhi = ICP_LEVEL4; | ||
438 | state->icp_auto_mlow = ICP_LEVEL3; | ||
439 | state->icp_auto_low = ICP_LEVEL2; | ||
440 | } | ||
441 | |||
442 | state->icp_dig = ICP_LEVEL3; | ||
443 | state->icp_man = ICP_LEVEL1; | ||
444 | state->acp_on = 1; | ||
445 | state->vco_mode = 0; | ||
446 | state->vco_shift = 0; | ||
447 | state->vco_band = VCOBANDSEL_1; | ||
448 | state->bs_delay = 8; | ||
449 | state->bs_freqcnt = 0x0fff; | ||
450 | state->bs_rdiv = 0x0fff; | ||
451 | state->prescaler_mode = 0; | ||
452 | state->lna_gain = LNA_MAX_GAIN; | ||
453 | state->rfvga_bias_ctrl = 1; | ||
454 | state->Fwindow_enabled = 1; | ||
455 | |||
456 | cx24113_set_Fref(state, 0); | ||
457 | cx24113_enable(state, 0x3d); | ||
458 | cx24113_set_parameters(state); | ||
459 | |||
460 | cx24113_set_gain_settings(state, -30); | ||
461 | |||
462 | cx24113_set_bandwidth(state, 18025); | ||
463 | cx24113_set_clk_inversion(state, 1); | ||
464 | |||
465 | if (state->config->xtal_khz >= 40000) | ||
466 | ret = cx24113_writereg(state, 0x02, | ||
467 | (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); | ||
468 | else | ||
469 | ret = cx24113_writereg(state, 0x02, | ||
470 | (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); | ||
471 | |||
472 | return ret; | ||
473 | } | ||
474 | |||
475 | static int cx24113_set_params(struct dvb_frontend *fe, | ||
476 | struct dvb_frontend_parameters *p) | ||
477 | { | ||
478 | struct cx24113_state *state = fe->tuner_priv; | ||
479 | /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ | ||
480 | u32 roll_off = 675; | ||
481 | u32 bw; | ||
482 | |||
483 | bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000; | ||
484 | bw += (10000000/100) + 5; | ||
485 | bw /= 10; | ||
486 | bw += 1000; | ||
487 | cx24113_set_bandwidth(state, bw); | ||
488 | |||
489 | cx24113_set_frequency(state, p->frequency); | ||
490 | msleep(5); | ||
491 | return cx24113_get_status(fe, &bw); | ||
492 | } | ||
493 | |||
494 | static s8 cx24113_agc_table[2][10] = { | ||
495 | {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, | ||
496 | {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, | ||
497 | }; | ||
498 | |||
499 | void cx24113_agc_callback(struct dvb_frontend *fe) | ||
500 | { | ||
501 | struct cx24113_state *state = fe->tuner_priv; | ||
502 | s16 s, i; | ||
503 | if (!fe->ops.read_signal_strength) | ||
504 | return; | ||
505 | |||
506 | do { | ||
507 | /* this only works with the current CX24123 implementation */ | ||
508 | fe->ops.read_signal_strength(fe, (u16 *) &s); | ||
509 | s >>= 8; | ||
510 | dprintk("signal strength: %d\n", s); | ||
511 | for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) | ||
512 | if (cx24113_agc_table[state->gain_level][i] > s) | ||
513 | break; | ||
514 | s = -25 - i*5; | ||
515 | } while (cx24113_set_gain_settings(state, s)); | ||
516 | } | ||
517 | EXPORT_SYMBOL(cx24113_agc_callback); | ||
518 | |||
519 | static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) | ||
520 | { | ||
521 | struct cx24113_state *state = fe->tuner_priv; | ||
522 | *frequency = state->frequency; | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int cx24113_release(struct dvb_frontend *fe) | ||
527 | { | ||
528 | struct cx24113_state *state = fe->tuner_priv; | ||
529 | dprintk("\n"); | ||
530 | fe->tuner_priv = NULL; | ||
531 | kfree(state); | ||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static const struct dvb_tuner_ops cx24113_tuner_ops = { | ||
536 | .info = { | ||
537 | .name = "Conexant CX24113", | ||
538 | .frequency_min = 950000, | ||
539 | .frequency_max = 2150000, | ||
540 | .frequency_step = 125, | ||
541 | }, | ||
542 | |||
543 | .release = cx24113_release, | ||
544 | |||
545 | .init = cx24113_init, | ||
546 | .sleep = NULL, | ||
547 | |||
548 | .set_params = cx24113_set_params, | ||
549 | .get_frequency = cx24113_get_frequency, | ||
550 | .get_bandwidth = NULL, | ||
551 | .get_status = cx24113_get_status, | ||
552 | }; | ||
553 | |||
554 | struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, | ||
555 | const struct cx24113_config *config, struct i2c_adapter *i2c) | ||
556 | { | ||
557 | /* allocate memory for the internal state */ | ||
558 | struct cx24113_state *state = | ||
559 | kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); | ||
560 | if (state == NULL) { | ||
561 | err("Unable to kmalloc\n"); | ||
562 | goto error; | ||
563 | } | ||
564 | |||
565 | /* setup the state */ | ||
566 | state->config = config; | ||
567 | state->i2c = i2c; | ||
568 | |||
569 | info("trying to detect myself\n"); | ||
570 | |||
571 | /* making a dummy read, because of some expected troubles | ||
572 | * after power on */ | ||
573 | cx24113_readreg(state, 0x00); | ||
574 | |||
575 | switch (state->rev = cx24113_readreg(state, 0x00)) { | ||
576 | case 0x43: | ||
577 | info("unknown device\n"); | ||
578 | break; | ||
579 | case REV_CX24113: | ||
580 | info("CX24113\n"); | ||
581 | break; | ||
582 | default: | ||
583 | err("unsupported revision: %x\n", state->rev); | ||
584 | goto error; | ||
585 | } | ||
586 | state->ver = cx24113_readreg(state, 0x01); | ||
587 | info("version: %x\n", state->ver); | ||
588 | |||
589 | /* create dvb_frontend */ | ||
590 | memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, | ||
591 | sizeof(struct dvb_tuner_ops)); | ||
592 | fe->tuner_priv = state; | ||
593 | return fe; | ||
594 | |||
595 | error: | ||
596 | kfree(state); | ||
597 | |||
598 | return NULL; | ||
599 | } | ||
600 | EXPORT_SYMBOL(cx24113_attach); | ||
601 | |||
602 | module_param(debug, int, 0644); | ||
603 | MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); | ||
604 | |||
605 | MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); | ||
606 | MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); | ||
607 | MODULE_LICENSE("GPL"); | ||
608 | |||