1 /*
2 * Fitipower FC0013 tuner driver
3 *
4 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
5 * partially based on driver code from Fitipower
6 * Copyright (C) 2010 Fitipower Integrated Technology Inc
7 *
8 * modified for use in librtlsdr
9 * Copyright (C) 2012 Steve Markgraf <steve@steve-m.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27 #include <stdint.h>
28 #include <stdio.h>
29
30 #include "rtlsdr_i2c.h"
31 #include "tuner_fc0013.h"
32
fc0013_writereg(void * dev,uint8_t reg,uint8_t val)33 static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val)
34 {
35 uint8_t data[2];
36 data[0] = reg;
37 data[1] = val;
38
39 if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0)
40 return -1;
41
42 return 0;
43 }
44
fc0013_readreg(void * dev,uint8_t reg,uint8_t * val)45 static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val)
46 {
47 uint8_t data = reg;
48
49 if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
50 return -1;
51
52 if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
53 return -1;
54
55 *val = data;
56
57 return 0;
58 }
59
fc0013_init(void * dev)60 int fc0013_init(void *dev)
61 {
62 int ret = 0;
63 unsigned int i;
64 uint8_t reg[] = {
65 0x00, /* reg. 0x00: dummy */
66 0x09, /* reg. 0x01 */
67 0x16, /* reg. 0x02 */
68 0x00, /* reg. 0x03 */
69 0x00, /* reg. 0x04 */
70 0x17, /* reg. 0x05 */
71 0x02, /* reg. 0x06: LPF bandwidth */
72 0x0a, /* reg. 0x07: CHECK */
73 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
74 Loop Bw 1/8 */
75 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */
76 0xb8, /* reg. 0x0a: Disable LO Test Buffer */
77 0x82, /* reg. 0x0b: CHECK */
78 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
79 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
80 0x00, /* reg. 0x0e */
81 0x00, /* reg. 0x0f */
82 0x00, /* reg. 0x10 */
83 0x00, /* reg. 0x11 */
84 0x00, /* reg. 0x12 */
85 0x00, /* reg. 0x13 */
86 0x50, /* reg. 0x14: DVB-t High Gain, UHF.
87 Middle Gain: 0x48, Low Gain: 0x40 */
88 0x01, /* reg. 0x15 */
89 };
90 #if 0
91 switch (rtlsdr_get_tuner_clock(dev)) {
92 case FC_XTAL_27_MHZ:
93 case FC_XTAL_28_8_MHZ:
94 reg[0x07] |= 0x20;
95 break;
96 case FC_XTAL_36_MHZ:
97 default:
98 break;
99 }
100 #endif
101 reg[0x07] |= 0x20;
102
103 // if (dev->dual_master)
104 reg[0x0c] |= 0x02;
105
106 for (i = 1; i < sizeof(reg); i++) {
107 ret = fc0013_writereg(dev, i, reg[i]);
108 if (ret < 0)
109 break;
110 }
111
112 return ret;
113 }
114
fc0013_rc_cal_add(void * dev,int rc_val)115 int fc0013_rc_cal_add(void *dev, int rc_val)
116 {
117 int ret;
118 uint8_t rc_cal;
119 int val;
120
121 /* push rc_cal value, get rc_cal value */
122 ret = fc0013_writereg(dev, 0x10, 0x00);
123 if (ret)
124 goto error_out;
125
126 /* get rc_cal value */
127 ret = fc0013_readreg(dev, 0x10, &rc_cal);
128 if (ret)
129 goto error_out;
130
131 rc_cal &= 0x0f;
132
133 val = (int)rc_cal + rc_val;
134
135 /* forcing rc_cal */
136 ret = fc0013_writereg(dev, 0x0d, 0x11);
137 if (ret)
138 goto error_out;
139
140 /* modify rc_cal value */
141 if (val > 15)
142 ret = fc0013_writereg(dev, 0x10, 0x0f);
143 else if (val < 0)
144 ret = fc0013_writereg(dev, 0x10, 0x00);
145 else
146 ret = fc0013_writereg(dev, 0x10, (uint8_t)val);
147
148 error_out:
149 return ret;
150 }
151
fc0013_rc_cal_reset(void * dev)152 int fc0013_rc_cal_reset(void *dev)
153 {
154 int ret;
155
156 ret = fc0013_writereg(dev, 0x0d, 0x01);
157 if (!ret)
158 ret = fc0013_writereg(dev, 0x10, 0x00);
159
160 return ret;
161 }
162
fc0013_set_vhf_track(void * dev,uint32_t freq)163 static int fc0013_set_vhf_track(void *dev, uint32_t freq)
164 {
165 int ret;
166 uint8_t tmp;
167
168 ret = fc0013_readreg(dev, 0x1d, &tmp);
169 if (ret)
170 goto error_out;
171 tmp &= 0xe3;
172 if (freq <= 177500000) { /* VHF Track: 7 */
173 ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
174 } else if (freq <= 184500000) { /* VHF Track: 6 */
175 ret = fc0013_writereg(dev, 0x1d, tmp | 0x18);
176 } else if (freq <= 191500000) { /* VHF Track: 5 */
177 ret = fc0013_writereg(dev, 0x1d, tmp | 0x14);
178 } else if (freq <= 198500000) { /* VHF Track: 4 */
179 ret = fc0013_writereg(dev, 0x1d, tmp | 0x10);
180 } else if (freq <= 205500000) { /* VHF Track: 3 */
181 ret = fc0013_writereg(dev, 0x1d, tmp | 0x0c);
182 } else if (freq <= 219500000) { /* VHF Track: 2 */
183 ret = fc0013_writereg(dev, 0x1d, tmp | 0x08);
184 } else if (freq < 300000000) { /* VHF Track: 1 */
185 ret = fc0013_writereg(dev, 0x1d, tmp | 0x04);
186 } else { /* UHF and GPS */
187 ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
188 }
189
190 error_out:
191 return ret;
192 }
193
fc0013_set_params(void * dev,uint32_t freq,uint32_t bandwidth)194 int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth)
195 {
196 int i, ret = 0;
197 uint8_t reg[7], am, pm, multi, tmp;
198 uint64_t f_vco;
199 uint32_t xtal_freq_div_2;
200 uint16_t xin, xdiv;
201 int vco_select = 0;
202
203 xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2;
204
205 /* set VHF track */
206 ret = fc0013_set_vhf_track(dev, freq);
207 if (ret)
208 goto exit;
209
210 if (freq < 300000000) {
211 /* enable VHF filter */
212 ret = fc0013_readreg(dev, 0x07, &tmp);
213 if (ret)
214 goto exit;
215 ret = fc0013_writereg(dev, 0x07, tmp | 0x10);
216 if (ret)
217 goto exit;
218
219 /* disable UHF & disable GPS */
220 ret = fc0013_readreg(dev, 0x14, &tmp);
221 if (ret)
222 goto exit;
223 ret = fc0013_writereg(dev, 0x14, tmp & 0x1f);
224 if (ret)
225 goto exit;
226 } else if (freq <= 862000000) {
227 /* disable VHF filter */
228 ret = fc0013_readreg(dev, 0x07, &tmp);
229 if (ret)
230 goto exit;
231 ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
232 if (ret)
233 goto exit;
234
235 /* enable UHF & disable GPS */
236 ret = fc0013_readreg(dev, 0x14, &tmp);
237 if (ret)
238 goto exit;
239 ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x40);
240 if (ret)
241 goto exit;
242 } else {
243 /* disable VHF filter */
244 ret = fc0013_readreg(dev, 0x07, &tmp);
245 if (ret)
246 goto exit;
247 ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
248 if (ret)
249 goto exit;
250
251 /* disable UHF & enable GPS */
252 ret = fc0013_readreg(dev, 0x14, &tmp);
253 if (ret)
254 goto exit;
255 ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x20);
256 if (ret)
257 goto exit;
258 }
259
260 /* select frequency divider and the frequency of VCO */
261 if (freq < 37084000) { /* freq * 96 < 3560000000 */
262 multi = 96;
263 reg[5] = 0x82;
264 reg[6] = 0x00;
265 } else if (freq < 55625000) { /* freq * 64 < 3560000000 */
266 multi = 64;
267 reg[5] = 0x02;
268 reg[6] = 0x02;
269 } else if (freq < 74167000) { /* freq * 48 < 3560000000 */
270 multi = 48;
271 reg[5] = 0x42;
272 reg[6] = 0x00;
273 } else if (freq < 111250000) { /* freq * 32 < 3560000000 */
274 multi = 32;
275 reg[5] = 0x82;
276 reg[6] = 0x02;
277 } else if (freq < 148334000) { /* freq * 24 < 3560000000 */
278 multi = 24;
279 reg[5] = 0x22;
280 reg[6] = 0x00;
281 } else if (freq < 222500000) { /* freq * 16 < 3560000000 */
282 multi = 16;
283 reg[5] = 0x42;
284 reg[6] = 0x02;
285 } else if (freq < 296667000) { /* freq * 12 < 3560000000 */
286 multi = 12;
287 reg[5] = 0x12;
288 reg[6] = 0x00;
289 } else if (freq < 445000000) { /* freq * 8 < 3560000000 */
290 multi = 8;
291 reg[5] = 0x22;
292 reg[6] = 0x02;
293 } else if (freq < 593334000) { /* freq * 6 < 3560000000 */
294 multi = 6;
295 reg[5] = 0x0a;
296 reg[6] = 0x00;
297 } else if (freq < 950000000) { /* freq * 4 < 3800000000 */
298 multi = 4;
299 reg[5] = 0x12;
300 reg[6] = 0x02;
301 } else {
302 multi = 2;
303 reg[5] = 0x0a;
304 reg[6] = 0x02;
305 }
306
307 f_vco = freq * multi;
308
309 if (f_vco >= 3060000000U) {
310 reg[6] |= 0x08;
311 vco_select = 1;
312 }
313
314 /* From divided value (XDIV) determined the FA and FP value */
315 xdiv = (uint16_t)(f_vco / xtal_freq_div_2);
316 if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2))
317 xdiv++;
318
319 pm = (uint8_t)(xdiv / 8);
320 am = (uint8_t)(xdiv - (8 * pm));
321
322 if (am < 2) {
323 am += 8;
324 pm--;
325 }
326
327 if (pm > 31) {
328 reg[1] = am + (8 * (pm - 31));
329 reg[2] = 31;
330 } else {
331 reg[1] = am;
332 reg[2] = pm;
333 }
334
335 if ((reg[1] > 15) || (reg[2] < 0x0b)) {
336 fprintf(stderr, "[FC0013] no valid PLL combination "
337 "found for %u Hz!\n", freq);
338 return -1;
339 }
340
341 /* fix clock out */
342 reg[6] |= 0x20;
343
344 /* From VCO frequency determines the XIN ( fractional part of Delta
345 Sigma PLL) and divided value (XDIV) */
346 xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000);
347 xin = (xin << 15) / (xtal_freq_div_2 / 1000);
348 if (xin >= 16384)
349 xin += 32768;
350
351 reg[3] = xin >> 8;
352 reg[4] = xin & 0xff;
353
354 reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
355 switch (bandwidth) {
356 case 6000000:
357 reg[6] |= 0x80;
358 break;
359 case 7000000:
360 reg[6] |= 0x40;
361 break;
362 case 8000000:
363 default:
364 break;
365 }
366
367 /* modified for Realtek demod */
368 reg[5] |= 0x07;
369
370 for (i = 1; i <= 6; i++) {
371 ret = fc0013_writereg(dev, i, reg[i]);
372 if (ret)
373 goto exit;
374 }
375
376 ret = fc0013_readreg(dev, 0x11, &tmp);
377 if (ret)
378 goto exit;
379 if (multi == 64)
380 ret = fc0013_writereg(dev, 0x11, tmp | 0x04);
381 else
382 ret = fc0013_writereg(dev, 0x11, tmp & 0xfb);
383 if (ret)
384 goto exit;
385
386 /* VCO Calibration */
387 ret = fc0013_writereg(dev, 0x0e, 0x80);
388 if (!ret)
389 ret = fc0013_writereg(dev, 0x0e, 0x00);
390
391 /* VCO Re-Calibration if needed */
392 if (!ret)
393 ret = fc0013_writereg(dev, 0x0e, 0x00);
394
395 if (!ret) {
396 // msleep(10);
397 ret = fc0013_readreg(dev, 0x0e, &tmp);
398 }
399 if (ret)
400 goto exit;
401
402 /* vco selection */
403 tmp &= 0x3f;
404
405 if (vco_select) {
406 if (tmp > 0x3c) {
407 reg[6] &= ~0x08;
408 ret = fc0013_writereg(dev, 0x06, reg[6]);
409 if (!ret)
410 ret = fc0013_writereg(dev, 0x0e, 0x80);
411 if (!ret)
412 ret = fc0013_writereg(dev, 0x0e, 0x00);
413 }
414 } else {
415 if (tmp < 0x02) {
416 reg[6] |= 0x08;
417 ret = fc0013_writereg(dev, 0x06, reg[6]);
418 if (!ret)
419 ret = fc0013_writereg(dev, 0x0e, 0x80);
420 if (!ret)
421 ret = fc0013_writereg(dev, 0x0e, 0x00);
422 }
423 }
424
425 exit:
426 return ret;
427 }
428
fc0013_set_gain_mode(void * dev,int manual)429 int fc0013_set_gain_mode(void *dev, int manual)
430 {
431 int ret = 0;
432 uint8_t tmp = 0;
433
434 ret |= fc0013_readreg(dev, 0x0d, &tmp);
435
436 if (manual)
437 tmp |= (1 << 3);
438 else
439 tmp &= ~(1 << 3);
440
441 ret |= fc0013_writereg(dev, 0x0d, tmp);
442
443 /* set a fixed IF-gain for now */
444 ret |= fc0013_writereg(dev, 0x13, 0x0a);
445
446 return ret;
447 }
448
449 int fc0013_lna_gains[] ={
450 -99, 0x02,
451 -73, 0x03,
452 -65, 0x05,
453 -63, 0x04,
454 -63, 0x00,
455 -60, 0x07,
456 -58, 0x01,
457 -54, 0x06,
458 58, 0x0f,
459 61, 0x0e,
460 63, 0x0d,
461 65, 0x0c,
462 67, 0x0b,
463 68, 0x0a,
464 70, 0x09,
465 71, 0x08,
466 179, 0x17,
467 181, 0x16,
468 182, 0x15,
469 184, 0x14,
470 186, 0x13,
471 188, 0x12,
472 191, 0x11,
473 197, 0x10
474 };
475
476 #define GAIN_CNT (sizeof(fc0013_lna_gains) / sizeof(int) / 2)
477
fc0013_set_lna_gain(void * dev,int gain)478 int fc0013_set_lna_gain(void *dev, int gain)
479 {
480 int ret = 0;
481 unsigned int i;
482 uint8_t tmp = 0;
483
484 ret |= fc0013_readreg(dev, 0x14, &tmp);
485
486 /* mask bits off */
487 tmp &= 0xe0;
488
489 for (i = 0; i < GAIN_CNT; i++) {
490 if ((fc0013_lna_gains[i*2] >= gain) || (i+1 == GAIN_CNT)) {
491 tmp |= fc0013_lna_gains[i*2 + 1];
492 break;
493 }
494 }
495
496 /* set gain */
497 ret |= fc0013_writereg(dev, 0x14, tmp);
498
499 return ret;
500 }
501