Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fc0011.c
Go to the documentation of this file.
1 /*
2  * Fitipower FC0011 tuner driver
3  *
4  * Copyright (C) 2012 Michael Buesch <[email protected]>
5  *
6  * Derived from FC0012 tuner driver:
7  * Copyright (C) 2012 Hans-Frieder Vogt <[email protected]>
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 {
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 */
39  FC11_REG_10, /* Unknown tuner reg 10 */
40  FC11_REG_11, /* Unknown tuner reg 11 */
42  FC11_REG_RCCAL, /* RC calibrate */
43  FC11_REG_VCOCAL, /* VCO calibrate */
45  FC11_REG_16, /* Unknown tuner reg 16 */
47 
48  FC11_NR_REGS, /* Number of registers */
49 };
50 
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 
60  FC11_RCCAL_FORCE = 0x10, /* force */
61 };
62 
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;
74 
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,
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,
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 {
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:
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:
276  break;
277  }
278 
279  /* Pre VCO select */
280  if (fvco < 2320000) {
281  vco_sel = 0;
283  } else if (fvco < 3080000) {
284  vco_sel = 1;
287  } else {
288  vco_sel = 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 */
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,
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) {
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 {
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) {
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) {
387  err = fc0011_writereg(priv, FC11_REG_VCOSEL,
388  regs[FC11_REG_VCOSEL]);
389  if (err)
390  return err;
391  } else {
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) {
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 {
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;
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 
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 }
521 
522 MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
523 MODULE_AUTHOR("Michael Buesch <[email protected]>");
524 MODULE_LICENSE("GPL");