1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include <linux/kernel.h>
28#include <linux/i2c.h>
29
30#include "dvb_frontend.h"
31
32#include "dib0070.h"
33#include "dibx000_common.h"
34
35static int debug;
36module_param(debug, int, 0644);
37MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
38
39#define dprintk(args...) do { \
40 if (debug) { \
41 printk(KERN_DEBUG "DiB0070: "); \
42 printk(args); \
43 printk("\n"); \
44 } \
45} while (0)
46
47#define DIB0070_P1D 0x00
48#define DIB0070_P1F 0x01
49#define DIB0070_P1G 0x03
50#define DIB0070S_P1A 0x02
51
52enum frontend_tune_state {
53 CT_TUNER_START = 10,
54 CT_TUNER_STEP_0,
55 CT_TUNER_STEP_1,
56 CT_TUNER_STEP_2,
57 CT_TUNER_STEP_3,
58 CT_TUNER_STEP_4,
59 CT_TUNER_STEP_5,
60 CT_TUNER_STEP_6,
61 CT_TUNER_STEP_7,
62 CT_TUNER_STOP,
63};
64
65#define FE_CALLBACK_TIME_NEVER 0xffffffff
66
67struct dib0070_state {
68 struct i2c_adapter *i2c;
69 struct dvb_frontend *fe;
70 const struct dib0070_config *cfg;
71 u16 wbd_ff_offset;
72 u8 revision;
73
74 enum frontend_tune_state tune_state;
75 u32 current_rf;
76
77
78 s8 step;
79 u16 adc_diff;
80
81 s8 captrim;
82 s8 fcaptrim;
83 u16 lo4;
84
85 const struct dib0070_tuning *current_tune_table_index;
86 const struct dib0070_lna_match *lna_match;
87
88 u8 wbd_gain_current;
89 u16 wbd_offset_3_3[2];
90};
91
92static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
93{
94 u8 b[2];
95 struct i2c_msg msg[2] = {
96 {.addr = state->cfg->i2c_address,.flags = 0,.buf = ®,.len = 1},
97 {.addr = state->cfg->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2},
98 };
99 if (i2c_transfer(state->i2c, msg, 2) != 2) {
100 printk(KERN_WARNING "DiB0070 I2C read failed\n");
101 return 0;
102 }
103 return (b[0] << 8) | b[1];
104}
105
106static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
107{
108 u8 b[3] = { reg, val >> 8, val & 0xff };
109 struct i2c_msg msg = {.addr = state->cfg->i2c_address,.flags = 0,.buf = b,.len = 3 };
110 if (i2c_transfer(state->i2c, &msg, 1) != 1) {
111 printk(KERN_WARNING "DiB0070 I2C write failed\n");
112 return -EREMOTEIO;
113 }
114 return 0;
115}
116
117#define HARD_RESET(state) do { \
118 state->cfg->sleep(state->fe, 0); \
119 if (state->cfg->reset) { \
120 state->cfg->reset(state->fe,1); msleep(10); \
121 state->cfg->reset(state->fe,0); msleep(10); \
122 } \
123} while (0)
124
125static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
126{
127 struct dib0070_state *state = fe->tuner_priv;
128 u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
129
130 if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 7000)
131 tmp |= (0 << 14);
132 else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 6000)
133 tmp |= (1 << 14);
134 else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 > 5000)
135 tmp |= (2 << 14);
136 else
137 tmp |= (3 << 14);
138
139 dib0070_write_reg(state, 0x02, tmp);
140
141
142 if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
143 u16 value = dib0070_read_reg(state, 0x17);
144
145 dib0070_write_reg(state, 0x17, value & 0xfffc);
146 tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
147 dib0070_write_reg(state, 0x01, tmp | (60 << 9));
148
149 dib0070_write_reg(state, 0x17, value);
150 }
151 return 0;
152}
153
154static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)
155{
156 int8_t step_sign;
157 u16 adc;
158 int ret = 0;
159
160 if (*tune_state == CT_TUNER_STEP_0) {
161
162 dib0070_write_reg(state, 0x0f, 0xed10);
163 dib0070_write_reg(state, 0x17, 0x0034);
164
165 dib0070_write_reg(state, 0x18, 0x0032);
166 state->step = state->captrim = state->fcaptrim = 64;
167 state->adc_diff = 3000;
168 ret = 20;
169
170 *tune_state = CT_TUNER_STEP_1;
171 } else if (*tune_state == CT_TUNER_STEP_1) {
172 state->step /= 2;
173 dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);
174 ret = 15;
175
176 *tune_state = CT_TUNER_STEP_2;
177 } else if (*tune_state == CT_TUNER_STEP_2) {
178
179 adc = dib0070_read_reg(state, 0x19);
180
181 dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc * (u32) 1800 / (u32) 1024);
182
183 if (adc >= 400) {
184 adc -= 400;
185 step_sign = -1;
186 } else {
187 adc = 400 - adc;
188 step_sign = 1;
189 }
190
191 if (adc < state->adc_diff) {
192 dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff);
193 state->adc_diff = adc;
194 state->fcaptrim = state->captrim;
195
196 }
197 state->captrim += (step_sign * state->step);
198
199 if (state->step >= 1)
200 *tune_state = CT_TUNER_STEP_1;
201 else
202 *tune_state = CT_TUNER_STEP_3;
203
204 } else if (*tune_state == CT_TUNER_STEP_3) {
205 dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);
206 dib0070_write_reg(state, 0x18, 0x07ff);
207 *tune_state = CT_TUNER_STEP_4;
208 }
209
210 return ret;
211}
212
213static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
214{
215 struct dib0070_state *state = fe->tuner_priv;
216 u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
217 dprintk("CTRL_LO5: 0x%x", lo5);
218 return dib0070_write_reg(state, 0x15, lo5);
219}
220
221void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
222{
223 struct dib0070_state *state = fe->tuner_priv;
224
225 if (open) {
226 dib0070_write_reg(state, 0x1b, 0xff00);
227 dib0070_write_reg(state, 0x1a, 0x0000);
228 } else {
229 dib0070_write_reg(state, 0x1b, 0x4112);
230 if (state->cfg->vga_filter != 0) {
231 dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
232 dprintk("vga filter register is set to %x", state->cfg->vga_filter);
233 } else
234 dib0070_write_reg(state, 0x1a, 0x0009);
235 }
236}
237
238EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
239struct dib0070_tuning {
240 u32 max_freq;
241 u8 switch_trim;
242 u8 vco_band;
243 u8 hfdiv;
244 u8 vco_multi;
245 u8 presc;
246 u8 wbdmux;
247 u16 tuner_enable;
248};
249
250struct dib0070_lna_match {
251 u32 max_freq;
252 u8 lna_band;
253};
254
255static const struct dib0070_tuning dib0070s_tuning_table[] = {
256 {570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800},
257 {700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800},
258 {863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800},
259 {1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400},
260 {1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400},
261 {2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400},
262 {0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000},
263};
264
265static const struct dib0070_tuning dib0070_tuning_table[] = {
266 {115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000},
267 {179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000},
268 {189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000},
269 {250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000},
270 {569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800},
271 {699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800},
272 {863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800},
273 {0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400},
274};
275
276static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
277 {180000, 0},
278 {188000, 1},
279 {196400, 2},
280 {250000, 3},
281 {550000, 0},
282 {590000, 1},
283 {666000, 3},
284 {864000, 5},
285 {1500000, 0},
286 {1600000, 1},
287 {2000000, 3},
288 {0xffffffff, 7},
289};
290
291static const struct dib0070_lna_match dib0070_lna[] = {
292 {180000, 0},
293 {188000, 1},
294 {196400, 2},
295 {250000, 3},
296 {550000, 2},
297 {650000, 3},
298 {750000, 5},
299 {850000, 6},
300 {864000, 7},
301 {1500000, 0},
302 {1600000, 1},
303 {2000000, 3},
304 {0xffffffff, 7},
305};
306
307#define LPF 100
308static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
309{
310 struct dib0070_state *state = fe->tuner_priv;
311
312 const struct dib0070_tuning *tune;
313 const struct dib0070_lna_match *lna_match;
314
315 enum frontend_tune_state *tune_state = &state->tune_state;
316 int ret = 10;
317
318 u8 band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
319 u32 freq = fe->dtv_property_cache.frequency / 1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
320
321#ifdef CONFIG_SYS_ISDBT
322 if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
323 if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
324 && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
325 || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
326 && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2)))
327 || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
328 && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
329 freq += 850;
330#endif
331 if (state->current_rf != freq) {
332
333 switch (state->revision) {
334 case DIB0070S_P1A:
335 tune = dib0070s_tuning_table;
336 lna_match = dib0070_lna;
337 break;
338 default:
339 tune = dib0070_tuning_table;
340 if (state->cfg->flip_chip)
341 lna_match = dib0070_lna_flip_chip;
342 else
343 lna_match = dib0070_lna;
344 break;
345 }
346 while (freq > tune->max_freq)
347 tune++;
348 while (freq > lna_match->max_freq)
349 lna_match++;
350
351 state->current_tune_table_index = tune;
352 state->lna_match = lna_match;
353 }
354
355 if (*tune_state == CT_TUNER_START) {
356 dprintk("Tuning for Band: %hd (%d kHz)", band, freq);
357 if (state->current_rf != freq) {
358 u8 REFDIV;
359 u32 FBDiv, Rest, FREF, VCOF_kHz;
360 u8 Den;
361
362 state->current_rf = freq;
363 state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
364
365 dib0070_write_reg(state, 0x17, 0x30);
366
367 VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
368
369 switch (band) {
370 case BAND_VHF:
371 REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
372 break;
373 case BAND_FM:
374 REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
375 break;
376 default:
377 REFDIV = (u8) (state->cfg->clock_khz / 10000);
378 break;
379 }
380 FREF = state->cfg->clock_khz / REFDIV;
381
382 switch (state->revision) {
383 case DIB0070S_P1A:
384 FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
385 Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
386 break;
387
388 case DIB0070_P1G:
389 case DIB0070_P1F:
390 default:
391 FBDiv = (freq / (FREF / 2));
392 Rest = 2 * freq - FBDiv * FREF;
393 break;
394 }
395
396 if (Rest < LPF)
397 Rest = 0;
398 else if (Rest < 2 * LPF)
399 Rest = 2 * LPF;
400 else if (Rest > (FREF - LPF)) {
401 Rest = 0;
402 FBDiv += 1;
403 } else if (Rest > (FREF - 2 * LPF))
404 Rest = FREF - 2 * LPF;
405 Rest = (Rest * 6528) / (FREF / 10);
406
407 Den = 1;
408 if (Rest > 0) {
409 state->lo4 |= (1 << 14) | (1 << 12);
410 Den = 255;
411 }
412
413 dib0070_write_reg(state, 0x11, (u16) FBDiv);
414 dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
415 dib0070_write_reg(state, 0x13, (u16) Rest);
416
417 if (state->revision == DIB0070S_P1A) {
418
419 if (band == BAND_SBAND) {
420 dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
421 dib0070_write_reg(state, 0x1d, 0xFFFF);
422 } else
423 dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
424 }
425
426 dib0070_write_reg(state, 0x20,
427 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
428
429 dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF);
430 dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest);
431 dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1);
432 dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv);
433 dprintk("VCO = %hd", state->current_tune_table_index->vco_band);
434 dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq);
435
436 *tune_state = CT_TUNER_STEP_0;
437 } else {
438 ret = 50;
439 *tune_state = CT_TUNER_STEP_5;
440 }
441 } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
442
443 ret = dib0070_captrim(state, tune_state);
444
445 } else if (*tune_state == CT_TUNER_STEP_4) {
446 const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
447 if (tmp != NULL) {
448 while (freq / 1000 > tmp->freq)
449 tmp++;
450 dib0070_write_reg(state, 0x0f,
451 (0 << 15) | (1 << 14) | (3 << 12) | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) | (state->
452 current_tune_table_index->
453 wbdmux << 0));
454 state->wbd_gain_current = tmp->wbd_gain_val;
455 } else {
456 dib0070_write_reg(state, 0x0f,
457 (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index->
458 wbdmux << 0));
459 state->wbd_gain_current = 6;
460 }
461
462 dib0070_write_reg(state, 0x06, 0x3fff);
463 dib0070_write_reg(state, 0x07,
464 (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
465 dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
466 dib0070_write_reg(state, 0x0d, 0x0d80);
467
468 dib0070_write_reg(state, 0x18, 0x07ff);
469 dib0070_write_reg(state, 0x17, 0x0033);
470
471 *tune_state = CT_TUNER_STEP_5;
472 } else if (*tune_state == CT_TUNER_STEP_5) {
473 dib0070_set_bandwidth(fe, ch);
474 *tune_state = CT_TUNER_STOP;
475 } else {
476 ret = FE_CALLBACK_TIME_NEVER;
477 }
478 return ret;
479}
480
481static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
482{
483 struct dib0070_state *state = fe->tuner_priv;
484 uint32_t ret;
485
486 state->tune_state = CT_TUNER_START;
487
488 do {
489 ret = dib0070_tune_digital(fe, p);
490 if (ret != FE_CALLBACK_TIME_NEVER)
491 msleep(ret / 10);
492 else
493 break;
494 } while (state->tune_state != CT_TUNER_STOP);
495
496 return 0;
497}
498
499static int dib0070_wakeup(struct dvb_frontend *fe)
500{
501 struct dib0070_state *state = fe->tuner_priv;
502 if (state->cfg->sleep)
503 state->cfg->sleep(fe, 0);
504 return 0;
505}
506
507static int dib0070_sleep(struct dvb_frontend *fe)
508{
509 struct dib0070_state *state = fe->tuner_priv;
510 if (state->cfg->sleep)
511 state->cfg->sleep(fe, 1);
512 return 0;
513}
514
515static const u16 dib0070_p1f_defaults[] = {
516 7, 0x02,
517 0x0008,
518 0x0000,
519 0x0000,
520 0x0000,
521 0x0000,
522 0x0002,
523 0x0100,
524
525 3, 0x0d,
526 0x0d80,
527 0x0001,
528 0x0000,
529
530 4, 0x11,
531 0x0000,
532 0x0103,
533 0x0000,
534 0x0000,
535
536 3, 0x16,
537 0x0004 | 0x0040,
538 0x0030,
539 0x07ff,
540
541 6, 0x1b,
542 0x4112,
543 0xff00,
544 0xc07f,
545 0x0000,
546 0x0180,
547 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
548
549 0,
550};
551
552static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
553{
554 u16 tuner_en = dib0070_read_reg(state, 0x20);
555 u16 offset;
556
557 dib0070_write_reg(state, 0x18, 0x07ff);
558 dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
559 dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
560 msleep(9);
561 offset = dib0070_read_reg(state, 0x19);
562 dib0070_write_reg(state, 0x20, tuner_en);
563 return offset;
564}
565
566static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
567{
568 u8 gain;
569 for (gain = 6; gain < 8; gain++) {
570 state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
571 dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain - 6]);
572 }
573}
574
575u16 dib0070_wbd_offset(struct dvb_frontend *fe)
576{
577 struct dib0070_state *state = fe->tuner_priv;
578 const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
579 u32 freq = fe->dtv_property_cache.frequency / 1000;
580
581 if (tmp != NULL) {
582 while (freq / 1000 > tmp->freq)
583 tmp++;
584 state->wbd_gain_current = tmp->wbd_gain_val;
585 } else
586 state->wbd_gain_current = 6;
587
588 return state->wbd_offset_3_3[state->wbd_gain_current - 6];
589}
590
591EXPORT_SYMBOL(dib0070_wbd_offset);
592
593#define pgm_read_word(w) (*w)
594static int dib0070_reset(struct dvb_frontend *fe)
595{
596 struct dib0070_state *state = fe->tuner_priv;
597 u16 l, r, *n;
598
599 HARD_RESET(state);
600
601#ifndef FORCE_SBAND_TUNER
602 if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
603 state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
604 else
605#else
606#warning forcing SBAND
607#endif
608 state->revision = DIB0070S_P1A;
609
610
611 dprintk("Revision: %x", state->revision);
612
613 if (state->revision == DIB0070_P1D) {
614 dprintk("Error: this driver is not to be used meant for P1D or earlier");
615 return -EINVAL;
616 }
617
618 n = (u16 *) dib0070_p1f_defaults;
619 l = pgm_read_word(n++);
620 while (l) {
621 r = pgm_read_word(n++);
622 do {
623 dib0070_write_reg(state, (u8) r, pgm_read_word(n++));
624 r++;
625 } while (--l);
626 l = pgm_read_word(n++);
627 }
628
629 if (state->cfg->force_crystal_mode != 0)
630 r = state->cfg->force_crystal_mode;
631 else if (state->cfg->clock_khz >= 24000)
632 r = 1;
633 else
634 r = 2;
635
636 r |= state->cfg->osc_buffer_state << 3;
637
638 dib0070_write_reg(state, 0x10, r);
639 dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
640
641 if (state->cfg->invert_iq) {
642 r = dib0070_read_reg(state, 0x02) & 0xffdf;
643 dib0070_write_reg(state, 0x02, r | (1 << 5));
644 }
645
646 if (state->revision == DIB0070S_P1A)
647 dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
648 else
649 dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter);
650
651 dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
652
653 dib0070_wbd_offset_calibration(state);
654
655 return 0;
656}
657
658static int dib0070_release(struct dvb_frontend *fe)
659{
660 kfree(fe->tuner_priv);
661 fe->tuner_priv = NULL;
662 return 0;
663}
664
665static const struct dvb_tuner_ops dib0070_ops = {
666 .info = {
667 .name = "DiBcom DiB0070",
668 .frequency_min = 45000000,
669 .frequency_max = 860000000,
670 .frequency_step = 1000,
671 },
672 .release = dib0070_release,
673
674 .init = dib0070_wakeup,
675 .sleep = dib0070_sleep,
676 .set_params = dib0070_tune,
677
678
679
680};
681
682struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
683{
684 struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
685 if (state == NULL)
686 return NULL;
687
688 state->cfg = cfg;
689 state->i2c = i2c;
690 state->fe = fe;
691 fe->tuner_priv = state;
692
693 if (dib0070_reset(fe) != 0)
694 goto free_mem;
695
696 printk(KERN_INFO "DiB0070: successfully identified\n");
697 memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
698
699 fe->tuner_priv = state;
700 return fe;
701
702 free_mem:
703 kfree(state);
704 fe->tuner_priv = NULL;
705 return NULL;
706}
707
708EXPORT_SYMBOL(dib0070_attach);
709
710MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
711MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
712MODULE_LICENSE("GPL");
713