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