1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Fitipower FC0011 tuner driver
5 * Copyright (C) 2012 Michael Buesch <m@bues.ch>
7 * Derived from FC0012 tuner driver:
8 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
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 */
26 FC11_REG_10
, /* Unknown tuner reg 10 */
27 FC11_REG_11
, /* Unknown tuner reg 11 */
29 FC11_REG_RCCAL
, /* RC calibrate */
30 FC11_REG_VCOCAL
, /* VCO calibrate */
32 FC11_REG_16
, /* Unknown tuner reg 16 */
35 FC11_NR_REGS
, /* Number of registers */
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 */
46 enum FC11_REG_RCCAL_bits
{
47 FC11_RCCAL_FORCE
= 0x10, /* force */
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 */
59 struct i2c_adapter
*i2c
;
67 static int fc0011_writereg(struct fc0011_priv
*priv
, u8 reg
, u8 val
)
69 u8 buf
[2] = { reg
, val
};
70 struct i2c_msg msg
= { .addr
= priv
->addr
,
71 .flags
= 0, .buf
= buf
, .len
= 2 };
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",
83 static int fc0011_readreg(struct fc0011_priv
*priv
, u8 reg
, u8
*val
)
86 struct i2c_msg msg
[2] = {
88 .flags
= 0, .buf
= ®
, .len
= 1 },
90 .flags
= I2C_M_RD
, .buf
= val
? : &dummy
, .len
= 1 },
93 if (i2c_transfer(priv
->i2c
, msg
, 2) != 2) {
94 dev_err(&priv
->i2c
->dev
,
95 "I2C read failed, reg: %02x\n", reg
);
102 static void fc0011_release(struct dvb_frontend
*fe
)
104 kfree(fe
->tuner_priv
);
105 fe
->tuner_priv
= NULL
;
108 static int fc0011_init(struct dvb_frontend
*fe
)
110 struct fc0011_priv
*priv
= fe
->tuner_priv
;
113 if (WARN_ON(!fe
->callback
))
116 err
= fe
->callback(priv
->i2c
, DVB_FRONTEND_COMPONENT_TUNER
,
117 FC0011_FE_CALLBACK_POWER
, priv
->addr
);
119 dev_err(&priv
->i2c
->dev
, "Power-on callback failed\n");
122 err
= fe
->callback(priv
->i2c
, DVB_FRONTEND_COMPONENT_TUNER
,
123 FC0011_FE_CALLBACK_RESET
, priv
->addr
);
125 dev_err(&priv
->i2c
->dev
, "Reset callback failed\n");
132 /* Initiate VCO calibration */
133 static int fc0011_vcocal_trigger(struct fc0011_priv
*priv
)
137 err
= fc0011_writereg(priv
, FC11_REG_VCOCAL
, FC11_VCOCAL_RESET
);
140 err
= fc0011_writereg(priv
, FC11_REG_VCOCAL
, FC11_VCOCAL_RUN
);
147 /* Read VCO calibration value */
148 static int fc0011_vcocal_read(struct fc0011_priv
*priv
, u8
*value
)
152 err
= fc0011_writereg(priv
, FC11_REG_VCOCAL
, FC11_VCOCAL_RUN
);
155 usleep_range(10000, 20000);
156 err
= fc0011_readreg(priv
, FC11_REG_VCOCAL
, value
);
163 static int fc0011_set_params(struct dvb_frontend
*fe
)
165 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
166 struct fc0011_priv
*priv
= fe
->tuner_priv
;
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
] = { };
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
]);
188 /* Set VCO freq and VCO div */
191 regs
[FC11_REG_VCO
] = 0x82;
192 } else if (freq
< 108000) {
194 regs
[FC11_REG_VCO
] = 0x42;
195 } else if (freq
< 216000) {
197 regs
[FC11_REG_VCO
] = 0x22;
198 } else if (freq
< 432000) {
200 regs
[FC11_REG_VCO
] = 0x12;
203 regs
[FC11_REG_VCO
] = 0x0A;
206 /* Calc XIN. The PLL reference frequency is 18 MHz. */
208 WARN_ON(xdiv
> 0xFF);
209 frac
= fvco
- xdiv
* 18000;
210 frac
= (frac
<< 15) / 18000;
216 xin
= clamp_t(u32
, frac
, 512, 65024);
217 regs
[FC11_REG_XINHI
] = xin
>> 8;
218 regs
[FC11_REG_XINLO
] = xin
;
222 if (fvco
- xdiv
* 18000 >= 9000)
223 xdivr
+= 1; /* round */
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
);
239 regs
[FC11_REG_FA
] = fa
;
240 regs
[FC11_REG_FP
] = fp
;
242 /* Select bandwidth */
247 regs
[FC11_REG_VCOSEL
] |= FC11_VCOSEL_BW7M
;
250 dev_warn(&priv
->i2c
->dev
, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n",
255 regs
[FC11_REG_VCOSEL
] |= FC11_VCOSEL_BW6M
;
260 if (fvco
< 2320000) {
262 regs
[FC11_REG_VCOSEL
] &= ~(FC11_VCOSEL_1
| FC11_VCOSEL_2
);
263 } else if (fvco
< 3080000) {
265 regs
[FC11_REG_VCOSEL
] &= ~(FC11_VCOSEL_1
| FC11_VCOSEL_2
);
266 regs
[FC11_REG_VCOSEL
] |= FC11_VCOSEL_1
;
269 regs
[FC11_REG_VCOSEL
] &= ~(FC11_VCOSEL_1
| FC11_VCOSEL_2
);
270 regs
[FC11_REG_VCOSEL
] |= FC11_VCOSEL_2
;
273 /* Fix for low freqs */
275 regs
[FC11_REG_FA
] = 0x6;
276 regs
[FC11_REG_FP
] = 0x11;
280 regs
[FC11_REG_VCOSEL
] |= FC11_VCOSEL_CLKOUT
;
282 /* Write the cached registers */
283 for (i
= FC11_REG_FA
; i
<= FC11_REG_VCOSEL
; i
++) {
284 err
= fc0011_writereg(priv
, i
, regs
[i
]);
289 /* VCO calibration */
290 err
= fc0011_vcocal_trigger(priv
);
293 err
= fc0011_vcocal_read(priv
, &vco_cal
);
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
);
302 dev_err(&priv
->i2c
->dev
, "Failed to reset tuner\n");
305 /* Reinit tuner config */
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
]);
316 /* VCO calibration */
317 err
= fc0011_vcocal_trigger(priv
);
320 err
= fc0011_vcocal_read(priv
, &vco_cal
);
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
);
331 vco_cal
&= FC11_VCOCAL_VALUEMASK
;
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
]);
345 err
= fc0011_vcocal_trigger(priv
);
349 regs
[FC11_REG_VCOSEL
] &= ~(FC11_VCOSEL_1
| FC11_VCOSEL_2
);
350 err
= fc0011_writereg(priv
, FC11_REG_VCOSEL
,
351 regs
[FC11_REG_VCOSEL
]);
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
]);
364 err
= fc0011_vcocal_trigger(priv
);
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
]);
375 regs
[FC11_REG_VCOSEL
] &= ~(FC11_VCOSEL_1
| FC11_VCOSEL_2
);
376 err
= fc0011_writereg(priv
, FC11_REG_VCOSEL
,
377 regs
[FC11_REG_VCOSEL
]);
380 err
= fc0011_vcocal_trigger(priv
);
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
]);
393 err
= fc0011_vcocal_trigger(priv
);
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
]);
406 err
= fc0011_vcocal_read(priv
, NULL
);
409 usleep_range(10000, 50000);
411 err
= fc0011_readreg(priv
, FC11_REG_RCCAL
, ®s
[FC11_REG_RCCAL
]);
414 regs
[FC11_REG_RCCAL
] |= FC11_RCCAL_FORCE
;
415 err
= fc0011_writereg(priv
, FC11_REG_RCCAL
, regs
[FC11_REG_RCCAL
]);
418 regs
[FC11_REG_16
] = 0xB;
419 err
= fc0011_writereg(priv
, FC11_REG_16
, regs
[FC11_REG_16
]);
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
);
433 priv
->frequency
= p
->frequency
;
434 priv
->bandwidth
= p
->bandwidth_hz
;
439 static int fc0011_get_frequency(struct dvb_frontend
*fe
, u32
*frequency
)
441 struct fc0011_priv
*priv
= fe
->tuner_priv
;
443 *frequency
= priv
->frequency
;
448 static int fc0011_get_if_frequency(struct dvb_frontend
*fe
, u32
*frequency
)
455 static int fc0011_get_bandwidth(struct dvb_frontend
*fe
, u32
*bandwidth
)
457 struct fc0011_priv
*priv
= fe
->tuner_priv
;
459 *bandwidth
= priv
->bandwidth
;
464 static const struct dvb_tuner_ops fc0011_tuner_ops
= {
466 .name
= "Fitipower FC0011",
468 .frequency_min_hz
= 45 * MHz
,
469 .frequency_max_hz
= 1000 * MHz
,
472 .release
= fc0011_release
,
475 .set_params
= fc0011_set_params
,
477 .get_frequency
= fc0011_get_frequency
,
478 .get_if_frequency
= fc0011_get_if_frequency
,
479 .get_bandwidth
= fc0011_get_bandwidth
,
482 struct dvb_frontend
*fc0011_attach(struct dvb_frontend
*fe
,
483 struct i2c_adapter
*i2c
,
484 const struct fc0011_config
*config
)
486 struct fc0011_priv
*priv
;
488 priv
= kzalloc(sizeof(struct fc0011_priv
), GFP_KERNEL
);
493 priv
->addr
= config
->i2c_address
;
495 fe
->tuner_priv
= priv
;
496 fe
->ops
.tuner_ops
= fc0011_tuner_ops
;
498 dev_info(&priv
->i2c
->dev
, "Fitipower FC0011 tuner attached\n");
502 EXPORT_SYMBOL(fc0011_attach
);
504 MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
505 MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
506 MODULE_LICENSE("GPL");