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 */
15enum {
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
38enum 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
46enum FC11_REG_RCCAL_bits {
47 FC11_RCCAL_FORCE = 0x10, /* force */
48};
49
50enum 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
58struct fc0011_priv {
59 struct i2c_adapter *i2c;
60 u8 addr;
61
62 u32 frequency;
63 u32 bandwidth;
64};
65
66
67static 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(adap: priv->i2c, msgs: &msg, num: 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
83static 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(adap: priv->i2c, msgs: msg, num: 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
102static void fc0011_release(struct dvb_frontend *fe)
103{
104 kfree(objp: fe->tuner_priv);
105 fe->tuner_priv = NULL;
106}
107
108static 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 */
133static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
134{
135 int err;
136
137 err = fc0011_writereg(priv, reg: FC11_REG_VCOCAL, val: FC11_VCOCAL_RESET);
138 if (err)
139 return err;
140 err = fc0011_writereg(priv, reg: FC11_REG_VCOCAL, val: FC11_VCOCAL_RUN);
141 if (err)
142 return err;
143
144 return 0;
145}
146
147/* Read VCO calibration value */
148static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
149{
150 int err;
151
152 err = fc0011_writereg(priv, reg: FC11_REG_VCOCAL, val: FC11_VCOCAL_RUN);
153 if (err)
154 return err;
155 usleep_range(min: 10000, max: 20000);
156 err = fc0011_readreg(priv, reg: FC11_REG_VCOCAL, val: value);
157 if (err)
158 return err;
159
160 return 0;
161}
162
163static 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, reg: FC11_REG_7, val: regs[FC11_REG_7]);
181 err |= fc0011_writereg(priv, reg: FC11_REG_8, val: regs[FC11_REG_8]);
182 err |= fc0011_writereg(priv, reg: FC11_REG_10, val: regs[FC11_REG_10]);
183 err |= fc0011_writereg(priv, reg: FC11_REG_11, val: regs[FC11_REG_11]);
184 err |= fc0011_writereg(priv, reg: FC11_REG_RCCAL, val: 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, reg: i, val: 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, value: &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, reg: i, val: regs[i]);
309 err |= fc0011_writereg(priv, reg: FC11_REG_7, val: regs[FC11_REG_7]);
310 err |= fc0011_writereg(priv, reg: FC11_REG_8, val: regs[FC11_REG_8]);
311 err |= fc0011_writereg(priv, reg: FC11_REG_10, val: regs[FC11_REG_10]);
312 err |= fc0011_writereg(priv, reg: FC11_REG_11, val: regs[FC11_REG_11]);
313 err |= fc0011_writereg(priv, reg: FC11_REG_RCCAL, val: 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, value: &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, reg: FC11_REG_VCOSEL,
342 val: 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, reg: FC11_REG_VCOSEL,
351 val: 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, reg: FC11_REG_VCOSEL,
361 val: 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, reg: FC11_REG_VCOSEL,
371 val: 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, reg: FC11_REG_VCOSEL,
377 val: 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, reg: FC11_REG_VCOSEL,
390 val: 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, reg: FC11_REG_VCOSEL,
400 val: 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(min: 10000, max: 50000);
410
411 err = fc0011_readreg(priv, reg: FC11_REG_RCCAL, val: &regs[FC11_REG_RCCAL]);
412 if (err)
413 return err;
414 regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
415 err = fc0011_writereg(priv, reg: FC11_REG_RCCAL, val: regs[FC11_REG_RCCAL]);
416 if (err)
417 return err;
418 regs[FC11_REG_16] = 0xB;
419 err = fc0011_writereg(priv, reg: FC11_REG_16, val: 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
439static 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
448static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
449{
450 *frequency = 0;
451
452 return 0;
453}
454
455static 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
464static 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
482struct 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(size: 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}
502EXPORT_SYMBOL_GPL(fc0011_attach);
503
504MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
505MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
506MODULE_LICENSE("GPL");
507

source code of linux/drivers/media/tuners/fc0011.c