1 /*
2  * Fitipower FC0012 tuner driver
3  *
4  * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
5  *
6  * modified for use in librtlsdr
7  * Copyright (C) 2012 Steve Markgraf <steve@steve-m.de>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include <stdint.h>
25 #include <stdio.h>
26 
27 #include "rtlsdr_i2c.h"
28 #include "tuner_fc0012.h"
29 
fc0012_writereg(void * dev,uint8_t reg,uint8_t val)30 static int fc0012_writereg(void *dev, uint8_t reg, uint8_t val)
31 {
32 	uint8_t data[2];
33 	data[0] = reg;
34 	data[1] = val;
35 
36 	if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 2) < 0)
37 		return -1;
38 
39 	return 0;
40 }
41 
fc0012_readreg(void * dev,uint8_t reg,uint8_t * val)42 static int fc0012_readreg(void *dev, uint8_t reg, uint8_t *val)
43 {
44 	uint8_t data = reg;
45 
46 	if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0)
47 		return -1;
48 
49 	if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0)
50 		return -1;
51 
52 	*val = data;
53 
54 	return 0;
55 }
56 
57 /* Incomplete list of register settings:
58  *
59  * Name			Reg	Bits	Desc
60  * CHIP_ID		0x00	0-7	Chip ID (constant 0xA1)
61  * RF_A			0x01	0-3	Number of count-to-9 cycles in RF
62  *					divider (suggested: 2..9)
63  * RF_M			0x02	0-7	Total number of cycles (to-8 and to-9)
64  *					in RF divider
65  * RF_K_HIGH		0x03	0-6	Bits 8..14 of fractional divider
66  * RF_K_LOW		0x04	0-7	Bits 0..7 of fractional RF divider
67  * RF_OUTDIV_A		0x05	3-7	Power of two required?
68  * LNA_POWER_DOWN	0x06	0	Set to 1 to switch off low noise amp
69  * RF_OUTDIV_B		0x06	1	Set to select 3 instead of 2 for the
70  *					RF output divider
71  * VCO_SPEED		0x06	3	Select tuning range of VCO:
72  *					 0 = Low range, (ca. 1.1 - 1.5GHz)
73  *					 1 = High range (ca. 1.4 - 1.8GHz)
74  * BANDWIDTH		0x06	6-7	Set bandwidth. 6MHz = 0x80, 7MHz=0x40
75  *					8MHz=0x00
76  * XTAL_SPEED		0x07	5	Set to 1 for 28.8MHz Crystal input
77  *					or 0 for 36MHz
78  * <agc params>		0x08	0-7
79  * EN_CAL_RSSI		0x09	4 	Enable calibrate RSSI
80  *					(Receive Signal Strength Indicator)
81  * LNA_FORCE		0x0d	0
82  * AGC_FORCE		0x0d	?
83  * LNA_GAIN		0x13	3-4	Low noise amp gain
84  * LNA_COMPS		0x15	3	?
85  * VCO_CALIB		0x0e	7	Set high then low to calibrate VCO
86  *					 (fast lock?)
87  * VCO_VOLTAGE		0x0e	0-6	Read Control voltage of VCO
88  *					 (big value -> low freq)
89  */
90 
fc0012_init(void * dev)91 int fc0012_init(void *dev)
92 {
93 	int ret = 0;
94 	unsigned int i;
95 	uint8_t reg[] = {
96 		0x00,	/* dummy reg. 0 */
97 		0x05,	/* reg. 0x01 */
98 		0x10,	/* reg. 0x02 */
99 		0x00,	/* reg. 0x03 */
100 		0x00,	/* reg. 0x04 */
101 		0x0f,	/* reg. 0x05: may also be 0x0a */
102 		0x00,	/* reg. 0x06: divider 2, VCO slow */
103 		0x00,	/* reg. 0x07: may also be 0x0f */
104 		0xff,	/* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
105 			   Loop Bw 1/8 */
106 		0x6e,	/* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */
107 		0xb8,	/* reg. 0x0a: Disable LO Test Buffer */
108 		0x82,	/* reg. 0x0b: Output Clock is same as clock frequency,
109 			   may also be 0x83 */
110 		0xfc,	/* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
111 		0x02,	/* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */
112 		0x00,	/* reg. 0x0e */
113 		0x00,	/* reg. 0x0f */
114 		0x00,	/* reg. 0x10: may also be 0x0d */
115 		0x00,	/* reg. 0x11 */
116 		0x1f,	/* reg. 0x12: Set to maximum gain */
117 		0x08,	/* reg. 0x13: Set to Middle Gain: 0x08,
118 			   Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */
119 		0x00,	/* reg. 0x14 */
120 		0x04,	/* reg. 0x15: Enable LNA COMPS */
121 	};
122 
123 #if 0
124 	switch (rtlsdr_get_tuner_clock(dev)) {
125 	case FC_XTAL_27_MHZ:
126 	case FC_XTAL_28_8_MHZ:
127 		reg[0x07] |= 0x20;
128 		break;
129 	case FC_XTAL_36_MHZ:
130 	default:
131 		break;
132 	}
133 #endif
134 	reg[0x07] |= 0x20;
135 
136 //	if (priv->dual_master)
137 	reg[0x0c] |= 0x02;
138 
139 	for (i = 1; i < sizeof(reg); i++) {
140 		ret = fc0012_writereg(dev, i, reg[i]);
141 		if (ret)
142 			break;
143 	}
144 
145 	return ret;
146 }
147 
fc0012_set_params(void * dev,uint32_t freq,uint32_t bandwidth)148 int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth)
149 {
150 	int i, ret = 0;
151 	uint8_t reg[7], am, pm, multi, tmp;
152 	uint64_t f_vco;
153 	uint32_t xtal_freq_div_2;
154 	uint16_t xin, xdiv;
155 	int vco_select = 0;
156 
157 	xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2;
158 
159 	/* select frequency divider and the frequency of VCO */
160 	if (freq < 37084000) {		/* freq * 96 < 3560000000 */
161 		multi = 96;
162 		reg[5] = 0x82;
163 		reg[6] = 0x00;
164 	} else if (freq < 55625000) {	/* freq * 64 < 3560000000 */
165 		multi = 64;
166 		reg[5] = 0x82;
167 		reg[6] = 0x02;
168 	} else if (freq < 74167000) {	/* freq * 48 < 3560000000 */
169 		multi = 48;
170 		reg[5] = 0x42;
171 		reg[6] = 0x00;
172 	} else if (freq < 111250000) {	/* freq * 32 < 3560000000 */
173 		multi = 32;
174 		reg[5] = 0x42;
175 		reg[6] = 0x02;
176 	} else if (freq < 148334000) {	/* freq * 24 < 3560000000 */
177 		multi = 24;
178 		reg[5] = 0x22;
179 		reg[6] = 0x00;
180 	} else if (freq < 222500000) {	/* freq * 16 < 3560000000 */
181 		multi = 16;
182 		reg[5] = 0x22;
183 		reg[6] = 0x02;
184 	} else if (freq < 296667000) {	/* freq * 12 < 3560000000 */
185 		multi = 12;
186 		reg[5] = 0x12;
187 		reg[6] = 0x00;
188 	} else if (freq < 445000000) {	/* freq * 8 < 3560000000 */
189 		multi = 8;
190 		reg[5] = 0x12;
191 		reg[6] = 0x02;
192 	} else if (freq < 593334000) {	/* freq * 6 < 3560000000 */
193 		multi = 6;
194 		reg[5] = 0x0a;
195 		reg[6] = 0x00;
196 	} else {
197 		multi = 4;
198 		reg[5] = 0x0a;
199 		reg[6] = 0x02;
200 	}
201 
202 	f_vco = freq * multi;
203 
204 	if (f_vco >= 3060000000U) {
205 		reg[6] |= 0x08;
206 		vco_select = 1;
207 	}
208 
209 	/* From divided value (XDIV) determined the FA and FP value */
210 	xdiv = (uint16_t)(f_vco / xtal_freq_div_2);
211 	if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2))
212 		xdiv++;
213 
214 	pm = (uint8_t)(xdiv / 8);
215 	am = (uint8_t)(xdiv - (8 * pm));
216 
217 	if (am < 2) {
218 		am += 8;
219 		pm--;
220 	}
221 
222 	if (pm > 31) {
223 		reg[1] = am + (8 * (pm - 31));
224 		reg[2] = 31;
225 	} else {
226 		reg[1] = am;
227 		reg[2] = pm;
228 	}
229 
230 	if ((reg[1] > 15) || (reg[2] < 0x0b)) {
231 		fprintf(stderr, "[FC0012] no valid PLL combination "
232 				"found for %u Hz!\n", freq);
233 		return -1;
234 	}
235 
236 	/* fix clock out */
237 	reg[6] |= 0x20;
238 
239 	/* From VCO frequency determines the XIN ( fractional part of Delta
240 	   Sigma PLL) and divided value (XDIV) */
241 	xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000);
242 	xin = (xin << 15) / (xtal_freq_div_2 / 1000);
243 	if (xin >= 16384)
244 		xin += 32768;
245 
246 	reg[3] = xin >> 8;	/* xin with 9 bit resolution */
247 	reg[4] = xin & 0xff;
248 
249 	reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
250 	switch (bandwidth) {
251 	case 6000000:
252 		reg[6] |= 0x80;
253 		break;
254 	case 7000000:
255 		reg[6] |= 0x40;
256 		break;
257 	case 8000000:
258 	default:
259 		break;
260 	}
261 
262 	/* modified for Realtek demod */
263 	reg[5] |= 0x07;
264 
265 	for (i = 1; i <= 6; i++) {
266 		ret = fc0012_writereg(dev, i, reg[i]);
267 		if (ret)
268 			goto exit;
269 	}
270 
271 	/* VCO Calibration */
272 	ret = fc0012_writereg(dev, 0x0e, 0x80);
273 	if (!ret)
274 		ret = fc0012_writereg(dev, 0x0e, 0x00);
275 
276 	/* VCO Re-Calibration if needed */
277 	if (!ret)
278 		ret = fc0012_writereg(dev, 0x0e, 0x00);
279 
280 	if (!ret) {
281 //		msleep(10);
282 		ret = fc0012_readreg(dev, 0x0e, &tmp);
283 	}
284 	if (ret)
285 		goto exit;
286 
287 	/* vco selection */
288 	tmp &= 0x3f;
289 
290 	if (vco_select) {
291 		if (tmp > 0x3c) {
292 			reg[6] &= ~0x08;
293 			ret = fc0012_writereg(dev, 0x06, reg[6]);
294 			if (!ret)
295 				ret = fc0012_writereg(dev, 0x0e, 0x80);
296 			if (!ret)
297 				ret = fc0012_writereg(dev, 0x0e, 0x00);
298 		}
299 	} else {
300 		if (tmp < 0x02) {
301 			reg[6] |= 0x08;
302 			ret = fc0012_writereg(dev, 0x06, reg[6]);
303 			if (!ret)
304 				ret = fc0012_writereg(dev, 0x0e, 0x80);
305 			if (!ret)
306 				ret = fc0012_writereg(dev, 0x0e, 0x00);
307 		}
308 	}
309 
310 exit:
311 	return ret;
312 }
313 
fc0012_set_gain(void * dev,int gain)314 int fc0012_set_gain(void *dev, int gain)
315 {
316 	int ret;
317 	uint8_t tmp = 0;
318 
319 	ret = fc0012_readreg(dev, 0x13, &tmp);
320 
321 	/* mask bits off */
322 	tmp &= 0xe0;
323 
324 	switch (gain) {
325 	case -99:		/* -9.9 dB */
326 		tmp |= 0x02;
327 		break;
328 	case -40:		/* -4 dB */
329 		break;
330 	case 71:
331 		tmp |= 0x08;	/* 7.1 dB */
332 		break;
333 	case 179:
334 		tmp |= 0x17;	/* 17.9 dB */
335 		break;
336 	case 192:
337 	default:
338 		tmp |= 0x10;	/* 19.2 dB */
339 		break;
340 	}
341 
342 	ret = fc0012_writereg(dev, 0x13, tmp);
343 
344 	return ret;
345 }
346