Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / drivers / media / tuners / fc0011.c
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
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
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
102 static void fc0011_release(struct dvb_frontend *fe)
103 {
104         kfree(fe->tuner_priv);
105         fe->tuner_priv = NULL;
106 }
107
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 */
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 */
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
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
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
448 static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
449 {
450         *frequency = 0;
451
452         return 0;
453 }
454
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
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(fc0011_attach);
503
504 MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
505 MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
506 MODULE_LICENSE("GPL");