xref: /openbmc/linux/drivers/media/tuners/fc0011.c (revision 0d456bad)
1 /*
2  * Fitipower FC0011 tuner driver
3  *
4  * Copyright (C) 2012 Michael Buesch <m@bues.ch>
5  *
6  * Derived from FC0012 tuner driver:
7  * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
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 "fc0011.h"
25 
26 
27 /* Tuner registers */
28 enum {
29 	FC11_REG_0,
30 	FC11_REG_FA,		/* FA */
31 	FC11_REG_FP,		/* FP */
32 	FC11_REG_XINHI,		/* XIN high 8 bit */
33 	FC11_REG_XINLO,		/* XIN low 8 bit */
34 	FC11_REG_VCO,		/* VCO */
35 	FC11_REG_VCOSEL,	/* VCO select */
36 	FC11_REG_7,		/* Unknown tuner reg 7 */
37 	FC11_REG_8,		/* Unknown tuner reg 8 */
38 	FC11_REG_9,
39 	FC11_REG_10,		/* Unknown tuner reg 10 */
40 	FC11_REG_11,		/* Unknown tuner reg 11 */
41 	FC11_REG_12,
42 	FC11_REG_RCCAL,		/* RC calibrate */
43 	FC11_REG_VCOCAL,	/* VCO calibrate */
44 	FC11_REG_15,
45 	FC11_REG_16,		/* Unknown tuner reg 16 */
46 	FC11_REG_17,
47 
48 	FC11_NR_REGS,		/* Number of registers */
49 };
50 
51 enum FC11_REG_VCOSEL_bits {
52 	FC11_VCOSEL_2		= 0x08, /* VCO select 2 */
53 	FC11_VCOSEL_1		= 0x10, /* VCO select 1 */
54 	FC11_VCOSEL_CLKOUT	= 0x20, /* Fix clock out */
55 	FC11_VCOSEL_BW7M	= 0x40, /* 7MHz bw */
56 	FC11_VCOSEL_BW6M	= 0x80, /* 6MHz bw */
57 };
58 
59 enum FC11_REG_RCCAL_bits {
60 	FC11_RCCAL_FORCE	= 0x10, /* force */
61 };
62 
63 enum FC11_REG_VCOCAL_bits {
64 	FC11_VCOCAL_RUN		= 0,	/* VCO calibration run */
65 	FC11_VCOCAL_VALUEMASK	= 0x3F,	/* VCO calibration value mask */
66 	FC11_VCOCAL_OK		= 0x40,	/* VCO calibration Ok */
67 	FC11_VCOCAL_RESET	= 0x80, /* VCO calibration reset */
68 };
69 
70 
71 struct fc0011_priv {
72 	struct i2c_adapter *i2c;
73 	u8 addr;
74 
75 	u32 frequency;
76 	u32 bandwidth;
77 };
78 
79 
80 static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
81 {
82 	u8 buf[2] = { reg, val };
83 	struct i2c_msg msg = { .addr = priv->addr,
84 		.flags = 0, .buf = buf, .len = 2 };
85 
86 	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
87 		dev_err(&priv->i2c->dev,
88 			"I2C write reg failed, reg: %02x, val: %02x\n",
89 			reg, val);
90 		return -EIO;
91 	}
92 
93 	return 0;
94 }
95 
96 static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
97 {
98 	u8 dummy;
99 	struct i2c_msg msg[2] = {
100 		{ .addr = priv->addr,
101 		  .flags = 0, .buf = &reg, .len = 1 },
102 		{ .addr = priv->addr,
103 		  .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
104 	};
105 
106 	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
107 		dev_err(&priv->i2c->dev,
108 			"I2C read failed, reg: %02x\n", reg);
109 		return -EIO;
110 	}
111 
112 	return 0;
113 }
114 
115 static int fc0011_release(struct dvb_frontend *fe)
116 {
117 	kfree(fe->tuner_priv);
118 	fe->tuner_priv = NULL;
119 
120 	return 0;
121 }
122 
123 static int fc0011_init(struct dvb_frontend *fe)
124 {
125 	struct fc0011_priv *priv = fe->tuner_priv;
126 	int err;
127 
128 	if (WARN_ON(!fe->callback))
129 		return -EINVAL;
130 
131 	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
132 			   FC0011_FE_CALLBACK_POWER, priv->addr);
133 	if (err) {
134 		dev_err(&priv->i2c->dev, "Power-on callback failed\n");
135 		return err;
136 	}
137 	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
138 			   FC0011_FE_CALLBACK_RESET, priv->addr);
139 	if (err) {
140 		dev_err(&priv->i2c->dev, "Reset callback failed\n");
141 		return err;
142 	}
143 
144 	return 0;
145 }
146 
147 /* Initiate VCO calibration */
148 static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
149 {
150 	int err;
151 
152 	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
153 	if (err)
154 		return err;
155 	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
156 	if (err)
157 		return err;
158 
159 	return 0;
160 }
161 
162 /* Read VCO calibration value */
163 static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
164 {
165 	int err;
166 
167 	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
168 	if (err)
169 		return err;
170 	usleep_range(10000, 20000);
171 	err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
172 	if (err)
173 		return err;
174 
175 	return 0;
176 }
177 
178 static int fc0011_set_params(struct dvb_frontend *fe)
179 {
180 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
181 	struct fc0011_priv *priv = fe->tuner_priv;
182 	int err;
183 	unsigned int i, vco_retries;
184 	u32 freq = p->frequency / 1000;
185 	u32 bandwidth = p->bandwidth_hz / 1000;
186 	u32 fvco, xin, xdiv, xdivr;
187 	u16 frac;
188 	u8 fa, fp, vco_sel, vco_cal;
189 	u8 regs[FC11_NR_REGS] = { };
190 
191 	regs[FC11_REG_7] = 0x0F;
192 	regs[FC11_REG_8] = 0x3E;
193 	regs[FC11_REG_10] = 0xB8;
194 	regs[FC11_REG_11] = 0x80;
195 	regs[FC11_REG_RCCAL] = 0x04;
196 	err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
197 	err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
198 	err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
199 	err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
200 	err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
201 	if (err)
202 		return -EIO;
203 
204 	/* Set VCO freq and VCO div */
205 	if (freq < 54000) {
206 		fvco = freq * 64;
207 		regs[FC11_REG_VCO] = 0x82;
208 	} else if (freq < 108000) {
209 		fvco = freq * 32;
210 		regs[FC11_REG_VCO] = 0x42;
211 	} else if (freq < 216000) {
212 		fvco = freq * 16;
213 		regs[FC11_REG_VCO] = 0x22;
214 	} else if (freq < 432000) {
215 		fvco = freq * 8;
216 		regs[FC11_REG_VCO] = 0x12;
217 	} else {
218 		fvco = freq * 4;
219 		regs[FC11_REG_VCO] = 0x0A;
220 	}
221 
222 	/* Calc XIN. The PLL reference frequency is 18 MHz. */
223 	xdiv = fvco / 18000;
224 	frac = fvco - xdiv * 18000;
225 	frac = (frac << 15) / 18000;
226 	if (frac >= 16384)
227 		frac += 32786;
228 	if (!frac)
229 		xin = 0;
230 	else if (frac < 511)
231 		xin = 512;
232 	else if (frac < 65026)
233 		xin = frac;
234 	else
235 		xin = 65024;
236 	regs[FC11_REG_XINHI] = xin >> 8;
237 	regs[FC11_REG_XINLO] = xin;
238 
239 	/* Calc FP and FA */
240 	xdivr = xdiv;
241 	if (fvco - xdiv * 18000 >= 9000)
242 		xdivr += 1; /* round */
243 	fp = xdivr / 8;
244 	fa = xdivr - fp * 8;
245 	if (fa < 2) {
246 		fp -= 1;
247 		fa += 8;
248 	}
249 	if (fp > 0x1F) {
250 		fp &= 0x1F;
251 		fa &= 0xF;
252 	}
253 	if (fa >= fp) {
254 		dev_warn(&priv->i2c->dev,
255 			 "fa %02X >= fp %02X, but trying to continue\n",
256 			 (unsigned int)(u8)fa, (unsigned int)(u8)fp);
257 	}
258 	regs[FC11_REG_FA] = fa;
259 	regs[FC11_REG_FP] = fp;
260 
261 	/* Select bandwidth */
262 	switch (bandwidth) {
263 	case 8000:
264 		break;
265 	case 7000:
266 		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
267 		break;
268 	default:
269 		dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. "
270 			 "Using 6000 kHz.\n",
271 			 bandwidth);
272 		bandwidth = 6000;
273 		/* fallthrough */
274 	case 6000:
275 		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
276 		break;
277 	}
278 
279 	/* Pre VCO select */
280 	if (fvco < 2320000) {
281 		vco_sel = 0;
282 		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
283 	} else if (fvco < 3080000) {
284 		vco_sel = 1;
285 		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
286 		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
287 	} else {
288 		vco_sel = 2;
289 		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
290 		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
291 	}
292 
293 	/* Fix for low freqs */
294 	if (freq < 45000) {
295 		regs[FC11_REG_FA] = 0x6;
296 		regs[FC11_REG_FP] = 0x11;
297 	}
298 
299 	/* Clock out fix */
300 	regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
301 
302 	/* Write the cached registers */
303 	for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
304 		err = fc0011_writereg(priv, i, regs[i]);
305 		if (err)
306 			return err;
307 	}
308 
309 	/* VCO calibration */
310 	err = fc0011_vcocal_trigger(priv);
311 	if (err)
312 		return err;
313 	err = fc0011_vcocal_read(priv, &vco_cal);
314 	if (err)
315 		return err;
316 	vco_retries = 0;
317 	while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
318 		/* Reset the tuner and try again */
319 		err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
320 				   FC0011_FE_CALLBACK_RESET, priv->addr);
321 		if (err) {
322 			dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
323 			return err;
324 		}
325 		/* Reinit tuner config */
326 		err = 0;
327 		for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
328 			err |= fc0011_writereg(priv, i, regs[i]);
329 		err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
330 		err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
331 		err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
332 		err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
333 		err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
334 		if (err)
335 			return -EIO;
336 		/* VCO calibration */
337 		err = fc0011_vcocal_trigger(priv);
338 		if (err)
339 			return err;
340 		err = fc0011_vcocal_read(priv, &vco_cal);
341 		if (err)
342 			return err;
343 		vco_retries++;
344 	}
345 	if (!(vco_cal & FC11_VCOCAL_OK)) {
346 		dev_err(&priv->i2c->dev,
347 			"Failed to read VCO calibration value (got %02X)\n",
348 			(unsigned int)vco_cal);
349 		return -EIO;
350 	}
351 	vco_cal &= FC11_VCOCAL_VALUEMASK;
352 
353 	switch (vco_sel) {
354 	case 0:
355 		if (vco_cal < 8) {
356 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
357 			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
358 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
359 					      regs[FC11_REG_VCOSEL]);
360 			if (err)
361 				return err;
362 			err = fc0011_vcocal_trigger(priv);
363 			if (err)
364 				return err;
365 		} else {
366 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
367 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
368 					      regs[FC11_REG_VCOSEL]);
369 			if (err)
370 				return err;
371 		}
372 		break;
373 	case 1:
374 		if (vco_cal < 5) {
375 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
376 			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
377 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
378 					      regs[FC11_REG_VCOSEL]);
379 			if (err)
380 				return err;
381 			err = fc0011_vcocal_trigger(priv);
382 			if (err)
383 				return err;
384 		} else if (vco_cal <= 48) {
385 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
386 			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
387 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
388 					      regs[FC11_REG_VCOSEL]);
389 			if (err)
390 				return err;
391 		} else {
392 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
393 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
394 					      regs[FC11_REG_VCOSEL]);
395 			if (err)
396 				return err;
397 			err = fc0011_vcocal_trigger(priv);
398 			if (err)
399 				return err;
400 		}
401 		break;
402 	case 2:
403 		if (vco_cal > 53) {
404 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
405 			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
406 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
407 					      regs[FC11_REG_VCOSEL]);
408 			if (err)
409 				return err;
410 			err = fc0011_vcocal_trigger(priv);
411 			if (err)
412 				return err;
413 		} else {
414 			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
415 			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
416 			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
417 					      regs[FC11_REG_VCOSEL]);
418 			if (err)
419 				return err;
420 		}
421 		break;
422 	}
423 	err = fc0011_vcocal_read(priv, NULL);
424 	if (err)
425 		return err;
426 	usleep_range(10000, 50000);
427 
428 	err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
429 	if (err)
430 		return err;
431 	regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
432 	err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
433 	if (err)
434 		return err;
435 	err = fc0011_writereg(priv, FC11_REG_16, 0xB);
436 	if (err)
437 		return err;
438 
439 	dev_dbg(&priv->i2c->dev, "Tuned to "
440 		"fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X "
441 		"vcocal=%02X(%u) bw=%u\n",
442 		(unsigned int)regs[FC11_REG_FA],
443 		(unsigned int)regs[FC11_REG_FP],
444 		(unsigned int)regs[FC11_REG_XINHI],
445 		(unsigned int)regs[FC11_REG_XINLO],
446 		(unsigned int)regs[FC11_REG_VCO],
447 		(unsigned int)regs[FC11_REG_VCOSEL],
448 		(unsigned int)vco_cal, vco_retries,
449 		(unsigned int)bandwidth);
450 
451 	priv->frequency = p->frequency;
452 	priv->bandwidth = p->bandwidth_hz;
453 
454 	return 0;
455 }
456 
457 static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
458 {
459 	struct fc0011_priv *priv = fe->tuner_priv;
460 
461 	*frequency = priv->frequency;
462 
463 	return 0;
464 }
465 
466 static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
467 {
468 	*frequency = 0;
469 
470 	return 0;
471 }
472 
473 static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
474 {
475 	struct fc0011_priv *priv = fe->tuner_priv;
476 
477 	*bandwidth = priv->bandwidth;
478 
479 	return 0;
480 }
481 
482 static const struct dvb_tuner_ops fc0011_tuner_ops = {
483 	.info = {
484 		.name		= "Fitipower FC0011",
485 
486 		.frequency_min	= 45000000,
487 		.frequency_max	= 1000000000,
488 	},
489 
490 	.release		= fc0011_release,
491 	.init			= fc0011_init,
492 
493 	.set_params		= fc0011_set_params,
494 
495 	.get_frequency		= fc0011_get_frequency,
496 	.get_if_frequency	= fc0011_get_if_frequency,
497 	.get_bandwidth		= fc0011_get_bandwidth,
498 };
499 
500 struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
501 				   struct i2c_adapter *i2c,
502 				   const struct fc0011_config *config)
503 {
504 	struct fc0011_priv *priv;
505 
506 	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
507 	if (!priv)
508 		return NULL;
509 
510 	priv->i2c = i2c;
511 	priv->addr = config->i2c_address;
512 
513 	fe->tuner_priv = priv;
514 	fe->ops.tuner_ops = fc0011_tuner_ops;
515 
516 	dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
517 
518 	return fe;
519 }
520 EXPORT_SYMBOL(fc0011_attach);
521 
522 MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
523 MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
524 MODULE_LICENSE("GPL");
525