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