tuner_fc0013.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. * Fitipower FC0013 tuner driver
  3. *
  4. * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
  5. * partially based on driver code from Fitipower
  6. * Copyright (C) 2010 Fitipower Integrated Technology Inc
  7. *
  8. * modified for use in librtlsdr
  9. * Copyright (C) 2012 Steve Markgraf <steve@steve-m.de>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. *
  25. */
  26. #include <stdint.h>
  27. #include <stdio.h>
  28. #include "rtlsdr_i2c.h"
  29. #include "tuner_fc0013.h"
  30. static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val)
  31. {
  32. uint8_t data[2];
  33. data[0] = reg;
  34. data[1] = val;
  35. if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0)
  36. return -1;
  37. return 0;
  38. }
  39. static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val)
  40. {
  41. uint8_t data = reg;
  42. if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
  43. return -1;
  44. if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
  45. return -1;
  46. *val = data;
  47. return 0;
  48. }
  49. int fc0013_init(void *dev)
  50. {
  51. int ret = 0;
  52. unsigned int i;
  53. uint8_t reg[] = {
  54. 0x00, /* reg. 0x00: dummy */
  55. 0x09, /* reg. 0x01 */
  56. 0x16, /* reg. 0x02 */
  57. 0x00, /* reg. 0x03 */
  58. 0x00, /* reg. 0x04 */
  59. 0x17, /* reg. 0x05 */
  60. 0x02, /* reg. 0x06: LPF bandwidth */
  61. 0x0a, /* reg. 0x07: CHECK */
  62. 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
  63. Loop Bw 1/8 */
  64. 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */
  65. 0xb8, /* reg. 0x0a: Disable LO Test Buffer */
  66. 0x82, /* reg. 0x0b: CHECK */
  67. 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
  68. 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
  69. 0x00, /* reg. 0x0e */
  70. 0x00, /* reg. 0x0f */
  71. 0x00, /* reg. 0x10 */
  72. 0x00, /* reg. 0x11 */
  73. 0x00, /* reg. 0x12 */
  74. 0x00, /* reg. 0x13 */
  75. 0x50, /* reg. 0x14: DVB-t High Gain, UHF.
  76. Middle Gain: 0x48, Low Gain: 0x40 */
  77. 0x01, /* reg. 0x15 */
  78. };
  79. #if 0
  80. switch (rtlsdr_get_tuner_clock(dev)) {
  81. case FC_XTAL_27_MHZ:
  82. case FC_XTAL_28_8_MHZ:
  83. reg[0x07] |= 0x20;
  84. break;
  85. case FC_XTAL_36_MHZ:
  86. default:
  87. break;
  88. }
  89. #endif
  90. reg[0x07] |= 0x20;
  91. // if (dev->dual_master)
  92. reg[0x0c] |= 0x02;
  93. for (i = 1; i < sizeof(reg); i++) {
  94. ret = fc0013_writereg(dev, i, reg[i]);
  95. if (ret < 0)
  96. break;
  97. }
  98. return ret;
  99. }
  100. int fc0013_rc_cal_add(void *dev, int rc_val)
  101. {
  102. int ret;
  103. uint8_t rc_cal;
  104. int val;
  105. /* push rc_cal value, get rc_cal value */
  106. ret = fc0013_writereg(dev, 0x10, 0x00);
  107. if (ret)
  108. goto error_out;
  109. /* get rc_cal value */
  110. ret = fc0013_readreg(dev, 0x10, &rc_cal);
  111. if (ret)
  112. goto error_out;
  113. rc_cal &= 0x0f;
  114. val = (int)rc_cal + rc_val;
  115. /* forcing rc_cal */
  116. ret = fc0013_writereg(dev, 0x0d, 0x11);
  117. if (ret)
  118. goto error_out;
  119. /* modify rc_cal value */
  120. if (val > 15)
  121. ret = fc0013_writereg(dev, 0x10, 0x0f);
  122. else if (val < 0)
  123. ret = fc0013_writereg(dev, 0x10, 0x00);
  124. else
  125. ret = fc0013_writereg(dev, 0x10, (uint8_t)val);
  126. error_out:
  127. return ret;
  128. }
  129. int fc0013_rc_cal_reset(void *dev)
  130. {
  131. int ret;
  132. ret = fc0013_writereg(dev, 0x0d, 0x01);
  133. if (!ret)
  134. ret = fc0013_writereg(dev, 0x10, 0x00);
  135. return ret;
  136. }
  137. static int fc0013_set_vhf_track(void *dev, uint32_t freq)
  138. {
  139. int ret;
  140. uint8_t tmp;
  141. ret = fc0013_readreg(dev, 0x1d, &tmp);
  142. if (ret)
  143. goto error_out;
  144. tmp &= 0xe3;
  145. if (freq <= 177500000) { /* VHF Track: 7 */
  146. ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
  147. } else if (freq <= 184500000) { /* VHF Track: 6 */
  148. ret = fc0013_writereg(dev, 0x1d, tmp | 0x18);
  149. } else if (freq <= 191500000) { /* VHF Track: 5 */
  150. ret = fc0013_writereg(dev, 0x1d, tmp | 0x14);
  151. } else if (freq <= 198500000) { /* VHF Track: 4 */
  152. ret = fc0013_writereg(dev, 0x1d, tmp | 0x10);
  153. } else if (freq <= 205500000) { /* VHF Track: 3 */
  154. ret = fc0013_writereg(dev, 0x1d, tmp | 0x0c);
  155. } else if (freq <= 219500000) { /* VHF Track: 2 */
  156. ret = fc0013_writereg(dev, 0x1d, tmp | 0x08);
  157. } else if (freq < 300000000) { /* VHF Track: 1 */
  158. ret = fc0013_writereg(dev, 0x1d, tmp | 0x04);
  159. } else { /* UHF and GPS */
  160. ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
  161. }
  162. error_out:
  163. return ret;
  164. }
  165. int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth)
  166. {
  167. int i, ret = 0;
  168. uint8_t reg[7], am, pm, multi, tmp;
  169. uint64_t f_vco;
  170. uint32_t xtal_freq_div_2;
  171. uint16_t xin, xdiv;
  172. int vco_select = 0;
  173. xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2;
  174. /* set VHF track */
  175. ret = fc0013_set_vhf_track(dev, freq);
  176. if (ret)
  177. goto exit;
  178. if (freq < 300000000) {
  179. /* enable VHF filter */
  180. ret = fc0013_readreg(dev, 0x07, &tmp);
  181. if (ret)
  182. goto exit;
  183. ret = fc0013_writereg(dev, 0x07, tmp | 0x10);
  184. if (ret)
  185. goto exit;
  186. /* disable UHF & disable GPS */
  187. ret = fc0013_readreg(dev, 0x14, &tmp);
  188. if (ret)
  189. goto exit;
  190. ret = fc0013_writereg(dev, 0x14, tmp & 0x1f);
  191. if (ret)
  192. goto exit;
  193. } else if (freq <= 862000000) {
  194. /* disable VHF filter */
  195. ret = fc0013_readreg(dev, 0x07, &tmp);
  196. if (ret)
  197. goto exit;
  198. ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
  199. if (ret)
  200. goto exit;
  201. /* enable UHF & disable GPS */
  202. ret = fc0013_readreg(dev, 0x14, &tmp);
  203. if (ret)
  204. goto exit;
  205. ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x40);
  206. if (ret)
  207. goto exit;
  208. } else {
  209. /* disable VHF filter */
  210. ret = fc0013_readreg(dev, 0x07, &tmp);
  211. if (ret)
  212. goto exit;
  213. ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
  214. if (ret)
  215. goto exit;
  216. /* disable UHF & enable GPS */
  217. ret = fc0013_readreg(dev, 0x14, &tmp);
  218. if (ret)
  219. goto exit;
  220. ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x20);
  221. if (ret)
  222. goto exit;
  223. }
  224. /* select frequency divider and the frequency of VCO */
  225. if (freq < 37084000) { /* freq * 96 < 3560000000 */
  226. multi = 96;
  227. reg[5] = 0x82;
  228. reg[6] = 0x00;
  229. } else if (freq < 55625000) { /* freq * 64 < 3560000000 */
  230. multi = 64;
  231. reg[5] = 0x02;
  232. reg[6] = 0x02;
  233. } else if (freq < 74167000) { /* freq * 48 < 3560000000 */
  234. multi = 48;
  235. reg[5] = 0x42;
  236. reg[6] = 0x00;
  237. } else if (freq < 111250000) { /* freq * 32 < 3560000000 */
  238. multi = 32;
  239. reg[5] = 0x82;
  240. reg[6] = 0x02;
  241. } else if (freq < 148334000) { /* freq * 24 < 3560000000 */
  242. multi = 24;
  243. reg[5] = 0x22;
  244. reg[6] = 0x00;
  245. } else if (freq < 222500000) { /* freq * 16 < 3560000000 */
  246. multi = 16;
  247. reg[5] = 0x42;
  248. reg[6] = 0x02;
  249. } else if (freq < 296667000) { /* freq * 12 < 3560000000 */
  250. multi = 12;
  251. reg[5] = 0x12;
  252. reg[6] = 0x00;
  253. } else if (freq < 445000000) { /* freq * 8 < 3560000000 */
  254. multi = 8;
  255. reg[5] = 0x22;
  256. reg[6] = 0x02;
  257. } else if (freq < 593334000) { /* freq * 6 < 3560000000 */
  258. multi = 6;
  259. reg[5] = 0x0a;
  260. reg[6] = 0x00;
  261. } else if (freq < 950000000) { /* freq * 4 < 3800000000 */
  262. multi = 4;
  263. reg[5] = 0x12;
  264. reg[6] = 0x02;
  265. } else {
  266. multi = 2;
  267. reg[5] = 0x0a;
  268. reg[6] = 0x02;
  269. }
  270. f_vco = freq * multi;
  271. if (f_vco >= 3060000000U) {
  272. reg[6] |= 0x08;
  273. vco_select = 1;
  274. }
  275. /* From divided value (XDIV) determined the FA and FP value */
  276. xdiv = (uint16_t)(f_vco / xtal_freq_div_2);
  277. if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2))
  278. xdiv++;
  279. pm = (uint8_t)(xdiv / 8);
  280. am = (uint8_t)(xdiv - (8 * pm));
  281. if (am < 2) {
  282. am += 8;
  283. pm--;
  284. }
  285. if (pm > 31) {
  286. reg[1] = am + (8 * (pm - 31));
  287. reg[2] = 31;
  288. } else {
  289. reg[1] = am;
  290. reg[2] = pm;
  291. }
  292. if ((reg[1] > 15) || (reg[2] < 0x0b)) {
  293. fprintf(stderr, "[FC0013] no valid PLL combination "
  294. "found for %u Hz!\n", freq);
  295. return -1;
  296. }
  297. /* fix clock out */
  298. reg[6] |= 0x20;
  299. /* From VCO frequency determines the XIN ( fractional part of Delta
  300. Sigma PLL) and divided value (XDIV) */
  301. xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000);
  302. xin = (xin << 15) / (xtal_freq_div_2 / 1000);
  303. if (xin >= 16384)
  304. xin += 32768;
  305. reg[3] = xin >> 8;
  306. reg[4] = xin & 0xff;
  307. reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
  308. switch (bandwidth) {
  309. case 6000000:
  310. reg[6] |= 0x80;
  311. break;
  312. case 7000000:
  313. reg[6] |= 0x40;
  314. break;
  315. case 8000000:
  316. default:
  317. break;
  318. }
  319. /* modified for Realtek demod */
  320. reg[5] |= 0x07;
  321. for (i = 1; i <= 6; i++) {
  322. ret = fc0013_writereg(dev, i, reg[i]);
  323. if (ret)
  324. goto exit;
  325. }
  326. ret = fc0013_readreg(dev, 0x11, &tmp);
  327. if (ret)
  328. goto exit;
  329. if (multi == 64)
  330. ret = fc0013_writereg(dev, 0x11, tmp | 0x04);
  331. else
  332. ret = fc0013_writereg(dev, 0x11, tmp & 0xfb);
  333. if (ret)
  334. goto exit;
  335. /* VCO Calibration */
  336. ret = fc0013_writereg(dev, 0x0e, 0x80);
  337. if (!ret)
  338. ret = fc0013_writereg(dev, 0x0e, 0x00);
  339. /* VCO Re-Calibration if needed */
  340. if (!ret)
  341. ret = fc0013_writereg(dev, 0x0e, 0x00);
  342. if (!ret) {
  343. // msleep(10);
  344. ret = fc0013_readreg(dev, 0x0e, &tmp);
  345. }
  346. if (ret)
  347. goto exit;
  348. /* vco selection */
  349. tmp &= 0x3f;
  350. if (vco_select) {
  351. if (tmp > 0x3c) {
  352. reg[6] &= ~0x08;
  353. ret = fc0013_writereg(dev, 0x06, reg[6]);
  354. if (!ret)
  355. ret = fc0013_writereg(dev, 0x0e, 0x80);
  356. if (!ret)
  357. ret = fc0013_writereg(dev, 0x0e, 0x00);
  358. }
  359. } else {
  360. if (tmp < 0x02) {
  361. reg[6] |= 0x08;
  362. ret = fc0013_writereg(dev, 0x06, reg[6]);
  363. if (!ret)
  364. ret = fc0013_writereg(dev, 0x0e, 0x80);
  365. if (!ret)
  366. ret = fc0013_writereg(dev, 0x0e, 0x00);
  367. }
  368. }
  369. exit:
  370. return ret;
  371. }
  372. int fc0013_set_gain_mode(void *dev, int manual)
  373. {
  374. int ret = 0;
  375. uint8_t tmp = 0;
  376. ret |= fc0013_readreg(dev, 0x0d, &tmp);
  377. if (manual)
  378. tmp |= (1 << 3);
  379. else
  380. tmp &= ~(1 << 3);
  381. ret |= fc0013_writereg(dev, 0x0d, tmp);
  382. /* set a fixed IF-gain for now */
  383. ret |= fc0013_writereg(dev, 0x13, 0x0a);
  384. return ret;
  385. }
  386. int fc0013_lna_gains[] ={
  387. -99, 0x02,
  388. -73, 0x03,
  389. -65, 0x05,
  390. -63, 0x04,
  391. -63, 0x00,
  392. -60, 0x07,
  393. -58, 0x01,
  394. -54, 0x06,
  395. 58, 0x0f,
  396. 61, 0x0e,
  397. 63, 0x0d,
  398. 65, 0x0c,
  399. 67, 0x0b,
  400. 68, 0x0a,
  401. 70, 0x09,
  402. 71, 0x08,
  403. 179, 0x17,
  404. 181, 0x16,
  405. 182, 0x15,
  406. 184, 0x14,
  407. 186, 0x13,
  408. 188, 0x12,
  409. 191, 0x11,
  410. 197, 0x10
  411. };
  412. #define GAIN_CNT (sizeof(fc0013_lna_gains) / sizeof(int) / 2)
  413. int fc0013_set_lna_gain(void *dev, int gain)
  414. {
  415. int ret = 0;
  416. unsigned int i;
  417. uint8_t tmp = 0;
  418. ret |= fc0013_readreg(dev, 0x14, &tmp);
  419. /* mask bits off */
  420. tmp &= 0xe0;
  421. for (i = 0; i < GAIN_CNT; i++) {
  422. if ((fc0013_lna_gains[i*2] >= gain) || (i+1 == GAIN_CNT)) {
  423. tmp |= fc0013_lna_gains[i*2 + 1];
  424. break;
  425. }
  426. }
  427. /* set gain */
  428. ret |= fc0013_writereg(dev, 0x14, tmp);
  429. return ret;
  430. }