78b696eefad8c5fcf9d979b69b754aa6fd365201
[rtl-433.git] / src / tuner_fc0013.c
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
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
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
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
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
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
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
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
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
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 }