[ARM] pxa: update defconfig for Verdex Pro
[linux-2.6/verdex.git] / drivers / media / dvb / frontends / dib0070.c
blob2be17b93e0bd7dcf2419b263316971304f30072f
1 /*
2 * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.
4 * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
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.
22 * This code is more or less generated from another driver, please
23 * excuse some codingstyle oddities.
27 #include <linux/kernel.h>
28 #include <linux/i2c.h>
30 #include "dvb_frontend.h"
32 #include "dib0070.h"
33 #include "dibx000_common.h"
35 static int debug;
36 module_param(debug, int, 0644);
37 MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
39 #define dprintk(args...) do { \
40 if (debug) { \
41 printk(KERN_DEBUG "DiB0070: "); \
42 printk(args); \
43 printk("\n"); \
44 } \
45 } while (0)
47 #define DIB0070_P1D 0x00
48 #define DIB0070_P1F 0x01
49 #define DIB0070_P1G 0x03
50 #define DIB0070S_P1A 0x02
52 enum 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,
65 #define FE_CALLBACK_TIME_NEVER 0xffffffff
67 struct 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;
74 enum frontend_tune_state tune_state;
75 u32 current_rf;
77 /* for the captrim binary search */
78 s8 step;
79 u16 adc_diff;
81 s8 captrim;
82 s8 fcaptrim;
83 u16 lo4;
85 const struct dib0070_tuning *current_tune_table_index;
86 const struct dib0070_lna_match *lna_match;
88 u8 wbd_gain_current;
89 u16 wbd_offset_3_3[2];
92 static uint16_t dib0070_read_reg(struct dib0070_state *state, u8 reg)
94 u8 b[2];
95 struct i2c_msg msg[2] = {
96 {.addr = state->cfg->i2c_address,.flags = 0,.buf = &reg,.len = 1},
97 {.addr = state->cfg->i2c_address,.flags = I2C_M_RD,.buf = b,.len = 2},
99 if (i2c_transfer(state->i2c, msg, 2) != 2) {
100 printk(KERN_WARNING "DiB0070 I2C read failed\n");
101 return 0;
103 return (b[0] << 8) | b[1];
106 static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
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;
114 return 0;
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); \
123 } while (0)
125 static int dib0070_set_bandwidth(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
127 struct dib0070_state *state = fe->tuner_priv;
128 u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
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);
139 dib0070_write_reg(state, 0x02, tmp);
141 /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
142 if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
143 u16 value = dib0070_read_reg(state, 0x17);
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));
149 dib0070_write_reg(state, 0x17, value);
151 return 0;
154 static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)
156 int8_t step_sign;
157 u16 adc;
158 int ret = 0;
160 if (*tune_state == CT_TUNER_STEP_0) {
162 dib0070_write_reg(state, 0x0f, 0xed10);
163 dib0070_write_reg(state, 0x17, 0x0034);
165 dib0070_write_reg(state, 0x18, 0x0032);
166 state->step = state->captrim = state->fcaptrim = 64;
167 state->adc_diff = 3000;
168 ret = 20;
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;
176 *tune_state = CT_TUNER_STEP_2;
177 } else if (*tune_state == CT_TUNER_STEP_2) {
179 adc = dib0070_read_reg(state, 0x19);
181 dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc * (u32) 1800 / (u32) 1024);
183 if (adc >= 400) {
184 adc -= 400;
185 step_sign = -1;
186 } else {
187 adc = 400 - adc;
188 step_sign = 1;
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;
197 state->captrim += (step_sign * state->step);
199 if (state->step >= 1)
200 *tune_state = CT_TUNER_STEP_1;
201 else
202 *tune_state = CT_TUNER_STEP_3;
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;
210 return ret;
213 static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
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);
221 void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
223 struct dib0070_state *state = fe->tuner_priv;
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);
238 EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
239 struct dib0070_tuning {
240 u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
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;
250 struct dib0070_lna_match {
251 u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
252 u8 lna_band;
255 static const struct dib0070_tuning dib0070s_tuning_table[] = {
256 {570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800}, /* UHF */
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}, /* LBAND */
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}, /* SBAND */
265 static const struct dib0070_tuning dib0070_tuning_table[] = {
266 {115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000}, /* FM below 92MHz cannot be tuned */
267 {179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000}, /* VHF */
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}, /* UHF */
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}, /* LBAND or everything higher than UHF */
276 static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
277 {180000, 0}, /* VHF */
278 {188000, 1},
279 {196400, 2},
280 {250000, 3},
281 {550000, 0}, /* UHF */
282 {590000, 1},
283 {666000, 3},
284 {864000, 5},
285 {1500000, 0}, /* LBAND or everything higher than UHF */
286 {1600000, 1},
287 {2000000, 3},
288 {0xffffffff, 7},
291 static const struct dib0070_lna_match dib0070_lna[] = {
292 {180000, 0}, /* VHF */
293 {188000, 1},
294 {196400, 2},
295 {250000, 3},
296 {550000, 2}, /* UHF */
297 {650000, 3},
298 {750000, 5},
299 {850000, 6},
300 {864000, 7},
301 {1500000, 0}, /* LBAND or everything higher than UHF */
302 {1600000, 1},
303 {2000000, 3},
304 {0xffffffff, 7},
307 #define LPF 100 // define for the loop filter 100kHz by default 16-07-06
308 static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_parameters *ch)
310 struct dib0070_state *state = fe->tuner_priv;
312 const struct dib0070_tuning *tune;
313 const struct dib0070_lna_match *lna_match;
315 enum frontend_tune_state *tune_state = &state->tune_state;
316 int ret = 10; /* 1ms is the default delay most of the time */
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);
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) {
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;
346 while (freq > tune->max_freq) /* find the right one */
347 tune++;
348 while (freq > lna_match->max_freq) /* find the right one */
349 lna_match++;
351 state->current_tune_table_index = tune;
352 state->lna_match = lna_match;
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;
362 state->current_rf = freq;
363 state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
365 dib0070_write_reg(state, 0x17, 0x30);
367 VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
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;
380 FREF = state->cfg->clock_khz / REFDIV;
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;
388 case DIB0070_P1G:
389 case DIB0070_P1F:
390 default:
391 FBDiv = (freq / (FREF / 2));
392 Rest = 2 * freq - FBDiv * FREF;
393 break;
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);
407 Den = 1;
408 if (Rest > 0) {
409 state->lo4 |= (1 << 14) | (1 << 12);
410 Den = 255;
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);
417 if (state->revision == DIB0070S_P1A) {
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);
426 dib0070_write_reg(state, 0x20,
427 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
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);
436 *tune_state = CT_TUNER_STEP_0;
437 } else { /* we are already tuned to this frequency - the configuration is correct */
438 ret = 50; /* wakeup time */
439 *tune_state = CT_TUNER_STEP_5;
441 } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
443 ret = dib0070_captrim(state, tune_state);
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) /* find the right one */
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;
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);
468 dib0070_write_reg(state, 0x18, 0x07ff);
469 dib0070_write_reg(state, 0x17, 0x0033);
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; /* tuner finished, time to call again infinite */
478 return ret;
481 static int dib0070_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
483 struct dib0070_state *state = fe->tuner_priv;
484 uint32_t ret;
486 state->tune_state = CT_TUNER_START;
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);
496 return 0;
499 static int dib0070_wakeup(struct dvb_frontend *fe)
501 struct dib0070_state *state = fe->tuner_priv;
502 if (state->cfg->sleep)
503 state->cfg->sleep(fe, 0);
504 return 0;
507 static int dib0070_sleep(struct dvb_frontend *fe)
509 struct dib0070_state *state = fe->tuner_priv;
510 if (state->cfg->sleep)
511 state->cfg->sleep(fe, 1);
512 return 0;
515 static const u16 dib0070_p1f_defaults[] = {
516 7, 0x02,
517 0x0008,
518 0x0000,
519 0x0000,
520 0x0000,
521 0x0000,
522 0x0002,
523 0x0100,
525 3, 0x0d,
526 0x0d80,
527 0x0001,
528 0x0000,
530 4, 0x11,
531 0x0000,
532 0x0103,
533 0x0000,
534 0x0000,
536 3, 0x16,
537 0x0004 | 0x0040,
538 0x0030,
539 0x07ff,
541 6, 0x1b,
542 0x4112,
543 0xff00,
544 0xc07f,
545 0x0000,
546 0x0180,
547 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
552 static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
554 u16 tuner_en = dib0070_read_reg(state, 0x20);
555 u16 offset;
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;
566 static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
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]);
575 u16 dib0070_wbd_offset(struct dvb_frontend *fe)
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;
581 if (tmp != NULL) {
582 while (freq / 1000 > tmp->freq) /* find the right one */
583 tmp++;
584 state->wbd_gain_current = tmp->wbd_gain_val;
585 } else
586 state->wbd_gain_current = 6;
588 return state->wbd_offset_3_3[state->wbd_gain_current - 6];
591 EXPORT_SYMBOL(dib0070_wbd_offset);
593 #define pgm_read_word(w) (*w)
594 static int dib0070_reset(struct dvb_frontend *fe)
596 struct dib0070_state *state = fe->tuner_priv;
597 u16 l, r, *n;
599 HARD_RESET(state);
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;
610 /* P1F or not */
611 dprintk("Revision: %x", state->revision);
613 if (state->revision == DIB0070_P1D) {
614 dprintk("Error: this driver is not to be used meant for P1D or earlier");
615 return -EINVAL;
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++);
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;
636 r |= state->cfg->osc_buffer_state << 3;
638 dib0070_write_reg(state, 0x10, r);
639 dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
641 if (state->cfg->invert_iq) {
642 r = dib0070_read_reg(state, 0x02) & 0xffdf;
643 dib0070_write_reg(state, 0x02, r | (1 << 5));
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);
651 dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
653 dib0070_wbd_offset_calibration(state);
655 return 0;
658 static int dib0070_release(struct dvb_frontend *fe)
660 kfree(fe->tuner_priv);
661 fe->tuner_priv = NULL;
662 return 0;
665 static 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,
672 .release = dib0070_release,
674 .init = dib0070_wakeup,
675 .sleep = dib0070_sleep,
676 .set_params = dib0070_tune,
678 // .get_frequency = dib0070_get_frequency,
679 // .get_bandwidth = dib0070_get_bandwidth
682 struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
684 struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
685 if (state == NULL)
686 return NULL;
688 state->cfg = cfg;
689 state->i2c = i2c;
690 state->fe = fe;
691 fe->tuner_priv = state;
693 if (dib0070_reset(fe) != 0)
694 goto free_mem;
696 printk(KERN_INFO "DiB0070: successfully identified\n");
697 memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
699 fe->tuner_priv = state;
700 return fe;
702 free_mem:
703 kfree(state);
704 fe->tuner_priv = NULL;
705 return NULL;
708 EXPORT_SYMBOL(dib0070_attach);
710 MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
711 MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
712 MODULE_LICENSE("GPL");