tuner_fc0012.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * fc0012 tuner support for rtl-sdr
  3. *
  4. * Based on tuner_fc0012.c found as part of the (seemingly GPLed)
  5. * rtl2832u Linux DVB driver.
  6. *
  7. * Rewritten and hacked into rtl-sdr by David Basden <davidb-sdr@rcpt.to>
  8. */
  9. #include <stdio.h>
  10. #include <stdint.h>
  11. #include "tuner_fc0012.h"
  12. #include "i2c.h"
  13. #define CRYSTAL_FREQ 28800000
  14. #define FC0012_LNAGAIN FC0012_LNA_GAIN_HI
  15. /* Incomplete list of register settings:
  16. *
  17. * Name Reg Bits Desc
  18. * LNA_POWER_DOWN 0x06 0 Set to 1 to switch off low noise amp
  19. * VCO_SPEED 0x06 3 Set the speed of the VCO. example
  20. * driver hardcodes to 1 for some reason
  21. * BANDWIDTH 0x06 6-7 Set bandwidth. 6MHz = 0x80, 7MHz=0x40
  22. * 8MHz=0x00
  23. * XTAL_SPEED 0x07 5 Set to 1 for 28.8MHz Crystal input
  24. * or 0 for 36MHz
  25. * EN_CAL_RSSI 0x09 4 Enable calibrate RSSI
  26. * (Receive Signal Strength Indicator)
  27. * LNA_FORCE 0x0d 0
  28. * AGC_FORCE 0x0d ?
  29. * LNA_GAIN 0x13 0-4 Low noise amp gain
  30. * LNA_COMPS 0x15 3 ?
  31. * VCO_CALIB 0x0e 7 Set high then low to calibrate VCO
  32. *
  33. */
  34. /* glue functions to rtl-sdr code */
  35. int FC0012_Write(void *pTuner, unsigned char RegAddr, unsigned char Byte)
  36. {
  37. uint8_t data[2];
  38. data[0] = RegAddr;
  39. data[1] = Byte;
  40. if (rtlsdr_i2c_write((rtlsdr_dev_t *)pTuner, FC0012_I2C_ADDR, data, 2) < 0)
  41. return FC0012_ERROR;
  42. return FC0012_OK;
  43. }
  44. int FC0012_Read(void *pTuner, unsigned char RegAddr, unsigned char *pByte)
  45. {
  46. uint8_t data = RegAddr;
  47. if (rtlsdr_i2c_write((rtlsdr_dev_t *)pTuner, FC0012_I2C_ADDR, &data, 1) < 0)
  48. return FC0012_ERROR;
  49. if (rtlsdr_i2c_read((rtlsdr_dev_t *)pTuner, FC0012_I2C_ADDR, &data, 1) < 0)
  50. return FC0012_ERROR;
  51. *pByte = data;
  52. return FC0012_OK;
  53. }
  54. #ifdef DEBUG
  55. #define DEBUGF printf
  56. #else
  57. #define DEBUGF(...) ()
  58. #endif
  59. #if 0
  60. void FC0012_Dump_Registers()
  61. {
  62. #ifdef DEBUG
  63. unsigned char regBuf;
  64. int ret;
  65. int i;
  66. DEBUGF("\nFC0012 registers:\n");
  67. for (i=0; i<=0x15; ++i)
  68. {
  69. ret = FC0012_Read(pTuner, i, &regBuf);
  70. if (ret) DEBUGF("\nCouldn't read register %02x\n", i);
  71. DEBUGF("R%x=%02x ",i,regBuf);
  72. }
  73. DEBUGF("\n");
  74. FC0012_Read(pTuner, 0x06, &regBuf);
  75. DEBUGF("LNA_POWER_DOWN:\t%s\n", regBuf & 1 ? "Powered down" : "Not Powered Down");
  76. DEBUGF("VCO_SPEED:\t%s\n", regBuf & 0x8 ? "High speed" : "Slow speed");
  77. DEBUGF("Bandwidth:\t%s\n", (regBuf & 0xC) ? "8MHz" : "less than 8MHz");
  78. FC0012_Read(pTuner, 0x07, &regBuf);
  79. DEBUGF("Crystal Speed:\t%s\n", (regBuf & 0x20) ? "28.8MHz" : "36MHZ<!>");
  80. FC0012_Read(pTuner, 0x09, &regBuf);
  81. DEBUGF("RSSI calibration mode:\t%s\n", (regBuf & 0x10) ? "RSSI CALIBRATION IN PROGRESS<!>" : "Disabled");
  82. FC0012_Read(pTuner, 0x0d, &regBuf);
  83. DEBUGF("LNA Force:\t%s\n", (regBuf & 0x1) ? "Forced" : "Not Forced");
  84. FC0012_Read(pTuner, 0x13, &regBuf);
  85. DEBUGF("LNA Gain:\t");
  86. switch (regBuf) {
  87. case (0x10): DEBUGF("19.7dB\n"); break;
  88. case (0x17): DEBUGF("17.9dB\n"); break;
  89. case (0x08): DEBUGF("7.1dB\n"); break;
  90. case (0x02): DEBUGF("-9.9dB\n"); break;
  91. default: DEBUGF("unknown gain value 0x02x\n");
  92. }
  93. #endif
  94. }
  95. #endif
  96. int FC0012_Open(void *pTuner)
  97. {
  98. // DEBUGF("FC0012_Open start");
  99. if (FC0012_Write(pTuner, 0x01, 0x05)) return -1;
  100. if (FC0012_Write(pTuner, 0x02, 0x10)) return -1;
  101. if (FC0012_Write(pTuner, 0x03, 0x00)) return -1;
  102. if (FC0012_Write(pTuner, 0x04, 0x00)) return -1;
  103. if (FC0012_Write(pTuner, 0x05, 0x0F)) return -1;
  104. if (FC0012_Write(pTuner, 0x06, 0x00)) return -1; // divider 2, VCO slow
  105. if (FC0012_Write(pTuner, 0x07, 0x20)) return -1; // change to 0x00 for a 36MHz crystal
  106. if (FC0012_Write(pTuner, 0x08, 0xFF)) return -1; // AGC Clock divide by 254, AGC gain 1/256, Loop Bw 1/8
  107. if (FC0012_Write(pTuner, 0x09, 0x6E)) return -1; // Disable LoopThrough
  108. if (FC0012_Write(pTuner, 0x0A, 0xB8)) return -1; // Disable LO Test Buffer
  109. if (FC0012_Write(pTuner, 0x0B, 0x82)) return -1; // Output Clock is same as clock frequency
  110. //if (FC0012_Write(pTuner, 0x0C, 0xF8)) return -1;
  111. if (FC0012_Write(pTuner, 0x0C, 0xFC)) return -1; // AGC up-down mode
  112. if (FC0012_Write(pTuner, 0x0D, 0x02)) return -1; // AGC Not Forcing & LNA Forcing
  113. if (FC0012_Write(pTuner, 0x0E, 0x00)) return -1;
  114. if (FC0012_Write(pTuner, 0x0F, 0x00)) return -1;
  115. if (FC0012_Write(pTuner, 0x10, 0x00)) return -1;
  116. if (FC0012_Write(pTuner, 0x11, 0x00)) return -1;
  117. if (FC0012_Write(pTuner, 0x12, 0x1F)) return -1; // Set to maximum gain
  118. if (FC0012_Write(pTuner, 0x13, FC0012_LNAGAIN)) return -1;
  119. if (FC0012_Write(pTuner, 0x14, 0x00)) return -1;
  120. if (FC0012_Write(pTuner, 0x15, 0x04)) return -1; // Enable LNA COMPS
  121. /* Black magic from nim_rtl2832_fc0012.c in DVB driver.
  122. Even though we've set 0x11 to 0x00 above, this needs to happen to have
  123. it go back
  124. */
  125. if (FC0012_Write(pTuner, 0x0d, 0x02)) return -1;
  126. if (FC0012_Write(pTuner, 0x11, 0x00)) return -1;
  127. if (FC0012_Write(pTuner, 0x15, 0x04)) return -1;
  128. // DEBUGF("FC0012_Open SUCCESS");
  129. return FC0012_OK;
  130. }
  131. # if 0
  132. // Frequency is in kHz. Bandwidth is in MHz
  133. // This is pseudocode to set GPIO6 for VHF/UHF filter switching.
  134. // Trying to do this in reality leads to fail currently. I'm probably doing it wrong.
  135. void FC0012_Frequency_Control(unsigned int Frequency, unsigned short Bandwidth)
  136. {
  137. if( Frequency < 260000 && Frequency > 150000 )
  138. {
  139. // set GPIO6 = low
  140. // 1. Set tuner frequency
  141. // 2. if the program quality is not good enough, switch to frequency + 500kHz
  142. // 3. if the program quality is still no good, switch to frequency - 500kHz
  143. }
  144. else
  145. {
  146. // set GPIO6 = high
  147. // set tuner frequency
  148. }
  149. }
  150. #endif
  151. int FC0012_SetFrequency(void *pTuner, unsigned long Frequency, unsigned short Bandwidth)
  152. {
  153. int VCO1 = 0;
  154. unsigned long doubleVCO;
  155. unsigned short xin, xdiv;
  156. unsigned char reg[21], am, pm, multi;
  157. unsigned char read_byte;
  158. unsigned long CrystalFreqKhz;
  159. // DEBUGF("FC0012_SetFrequency start");
  160. CrystalFreqKhz = (CRYSTAL_FREQ + 500) / 1000;
  161. //===================================== Select frequency divider and the frequency of VCO
  162. if (Frequency * 96 < 3560000)
  163. {
  164. multi = 96; reg[5] = 0x82; reg[6] = 0x00;
  165. }
  166. else if (Frequency * 64 < 3560000)
  167. {
  168. multi = 64; reg[5] = 0x82; reg[6] = 0x02;
  169. }
  170. else if (Frequency * 48 < 3560000)
  171. {
  172. multi = 48; reg[5] = 0x42; reg[6] = 0x00;
  173. }
  174. else if (Frequency * 32 < 3560000)
  175. {
  176. multi = 32; reg[5] = 0x42; reg[6] = 0x02;
  177. }
  178. else if (Frequency * 24 < 3560000)
  179. {
  180. multi = 24; reg[5] = 0x22; reg[6] = 0x00;
  181. }
  182. else if (Frequency * 16 < 3560000)
  183. {
  184. multi = 16; reg[5] = 0x22; reg[6] = 0x02;
  185. }
  186. else if (Frequency * 12 < 3560000)
  187. {
  188. multi = 12; reg[5] = 0x12; reg[6] = 0x00;
  189. }
  190. else if (Frequency * 8 < 3560000)
  191. {
  192. multi = 8; reg[5] = 0x12; reg[6] = 0x02;
  193. }
  194. else if (Frequency * 6 < 3560000)
  195. {
  196. multi = 6; reg[5] = 0x0A; reg[6] = 0x00;
  197. }
  198. else
  199. {
  200. multi = 4; reg[5] = 0x0A; reg[6] = 0x02;
  201. }
  202. doubleVCO = Frequency * multi;
  203. reg[6] = reg[6] | 0x08;
  204. VCO1 = 1;
  205. xdiv = (unsigned short)(doubleVCO / (CrystalFreqKhz / 2));
  206. if( (doubleVCO - xdiv * (CrystalFreqKhz / 2)) >= (CrystalFreqKhz / 4) )
  207. xdiv = xdiv + 1;
  208. pm = (unsigned char)( xdiv / 8 );
  209. am = (unsigned char)( xdiv - (8 * pm));
  210. if (am < 2) {
  211. reg[1] = am + 8;
  212. reg[2] = pm - 1;
  213. } else {
  214. reg[1] = am;
  215. reg[2] = pm;
  216. }
  217. // From VCO frequency determines the XIN ( fractional part of Delta Sigma PLL) and divided value (XDIV).
  218. xin = (unsigned short)(doubleVCO - ((unsigned short)(doubleVCO / (CrystalFreqKhz / 2))) * (CrystalFreqKhz / 2));
  219. xin = ((xin << 15)/(unsigned short)(CrystalFreqKhz / 2));
  220. if( xin >= (unsigned short) 16384 )
  221. xin = xin + (unsigned short) 32768;
  222. reg[3] = (unsigned char)(xin >> 8);
  223. reg[4] = (unsigned char)(xin & 0x00FF);
  224. // DEBUGF("Frequency: %lu, Fa: %d, Fp: %d, Xin:%d \n", Frequency, am, pm, xin);
  225. switch(Bandwidth)
  226. {
  227. case 6: reg[6] = 0x80 | reg[6]; break;
  228. case 7: reg[6] = (~0x80 & reg[6]) | 0x40; break;
  229. case 8: default: reg[6] = ~0xC0 & reg[6]; break;
  230. }
  231. if (FC0012_Write(pTuner, 0x01, reg[1])) return -1;
  232. if (FC0012_Write(pTuner, 0x02, reg[2])) return -1;
  233. if (FC0012_Write(pTuner, 0x03, reg[3])) return -1;
  234. if (FC0012_Write(pTuner, 0x04, reg[4])) return -1;
  235. //reg[5] = reg[5] | 0x07; // This is really not cool. Why is it there?
  236. // Same with hardcoding VCO=1
  237. if (FC0012_Write(pTuner, 0x05, reg[5])) return -1;
  238. if (FC0012_Write(pTuner, 0x06, reg[6])) return -1;
  239. // VCO Calibration
  240. if (FC0012_Write(pTuner, 0x0E, 0x80)) return -1;
  241. if (FC0012_Write(pTuner, 0x0E, 0x00)) return -1;
  242. // VCO Re-Calibration if needed
  243. if (FC0012_Write(pTuner, 0x0E, 0x00)) return -1;
  244. if (FC0012_Read(pTuner, 0x0E, &read_byte)) return -1;
  245. reg[14] = 0x3F & read_byte;
  246. if (VCO1)
  247. {
  248. if (reg[14] > 0x3C)
  249. {
  250. reg[6] = 0x08 | reg[6];
  251. if (FC0012_Write(pTuner, 0x06, reg[6])) return -1;
  252. if (FC0012_Write(pTuner, 0x0E, 0x80)) return -1;
  253. if (FC0012_Write(pTuner, 0x0E, 0x00)) return -1;
  254. }
  255. }
  256. else
  257. {
  258. if (reg[14] < 0x02) {
  259. reg[6] = 0x08 | reg[6];
  260. if (FC0012_Write(pTuner, 0x06, reg[6])) return -1;
  261. if (FC0012_Write(pTuner, 0x0E, 0x80)) return -1;
  262. if (FC0012_Write(pTuner, 0x0E, 0x00)) return -1;
  263. }
  264. }
  265. // DEBUGF("FC0012_SetFrequency SUCCESS"); FC0012_Dump_Registers();
  266. return FC0012_OK;
  267. }