fc0011.c 12 KB

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