2 * FCI FC2580 tuner driver, taken from the kernel driver that can be found
3 * on http://linux.terratec.de/tv_en.html
5 * This driver is a mess, and should be cleaned up/rewritten.
11 #include "rtlsdr_i2c.h"
12 #include "tuner_fc2580.h"
14 /* 16.384 MHz (at least on the Logilink VG0002A) */
15 #define CRYSTAL_FREQ 16384000
17 /* glue functions to rtl-sdr code */
19 fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val)
26 if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, data, 2) < 0)
27 return FC2580_FCI_FAIL;
29 return FC2580_FCI_SUCCESS;
32 fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data)
36 if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0)
37 return FC2580_FCI_FAIL;
39 if (rtlsdr_i2c_read_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0)
40 return FC2580_FCI_FAIL;
44 return FC2580_FCI_SUCCESS;
53 unsigned int CrystalFreqKhz;
56 AgcMode = FC2580_AGC_EXTERNAL;
58 // Initialize tuner with AGC mode.
59 // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000)
60 CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000);
62 if(fc2580_set_init(pTuner, AgcMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS)
63 goto error_status_initialize_tuner;
66 return FUNCTION_SUCCESS;
69 error_status_initialize_tuner:
70 return FUNCTION_ERROR;
76 unsigned long RfFreqHz
79 unsigned int RfFreqKhz;
80 unsigned int CrystalFreqKhz;
82 // Set tuner RF frequency in KHz.
83 // Note: RfFreqKhz = round(RfFreqHz / 1000)
84 // CrystalFreqKhz = round(CrystalFreqHz / 1000)
85 RfFreqKhz = (unsigned int)((RfFreqHz + 500) / 1000);
86 CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000);
88 if(fc2580_set_freq(pTuner, RfFreqKhz, CrystalFreqKhz) != FC2580_FCI_SUCCESS)
89 goto error_status_set_tuner_rf_frequency;
91 return FUNCTION_SUCCESS;
93 error_status_set_tuner_rf_frequency:
94 return FUNCTION_ERROR;
99 @brief Set FC2580 tuner bandwidth mode.
103 fc2580_SetBandwidthMode(
108 unsigned int CrystalFreqKhz;
110 // Set tuner bandwidth mode.
111 // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000)
112 CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000);
114 if(fc2580_set_filter(pTuner, (unsigned char)BandwidthMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS)
115 goto error_status_set_tuner_bandwidth_mode;
117 return FUNCTION_SUCCESS;
120 error_status_set_tuner_bandwidth_mode:
121 return FUNCTION_ERROR;
124 void fc2580_wait_msec(void *pTuner, int a)
126 /* USB latency is enough for now ;) */
131 /*==============================================================================
132 fc2580 initial setting
134 This function is a generic function which gets called to initialize
136 fc2580 in DVB-H mode or L-Band TDMB mode
143 2 : Voltage Control Mode
145 ==============================================================================*/
146 fc2580_fci_result_type fc2580_set_init( void *pTuner, int ifagc_mode, unsigned int freq_xtal )
148 fc2580_fci_result_type result = FC2580_FCI_SUCCESS;
150 result &= fc2580_i2c_write(pTuner, 0x00, 0x00); /*** Confidential ***/
151 result &= fc2580_i2c_write(pTuner, 0x12, 0x86);
152 result &= fc2580_i2c_write(pTuner, 0x14, 0x5C);
153 result &= fc2580_i2c_write(pTuner, 0x16, 0x3C);
154 result &= fc2580_i2c_write(pTuner, 0x1F, 0xD2);
155 result &= fc2580_i2c_write(pTuner, 0x09, 0xD7);
156 result &= fc2580_i2c_write(pTuner, 0x0B, 0xD5);
157 result &= fc2580_i2c_write(pTuner, 0x0C, 0x32);
158 result &= fc2580_i2c_write(pTuner, 0x0E, 0x43);
159 result &= fc2580_i2c_write(pTuner, 0x21, 0x0A);
160 result &= fc2580_i2c_write(pTuner, 0x22, 0x82);
161 if( ifagc_mode == 1 )
163 result &= fc2580_i2c_write(pTuner, 0x45, 0x10); //internal AGC
164 result &= fc2580_i2c_write(pTuner, 0x4C, 0x00); //HOLD_AGC polarity
166 else if( ifagc_mode == 2 )
168 result &= fc2580_i2c_write(pTuner, 0x45, 0x20); //Voltage Control Mode
169 result &= fc2580_i2c_write(pTuner, 0x4C, 0x02); //HOLD_AGC polarity
171 result &= fc2580_i2c_write(pTuner, 0x3F, 0x88);
172 result &= fc2580_i2c_write(pTuner, 0x02, 0x0E);
173 result &= fc2580_i2c_write(pTuner, 0x58, 0x14);
174 result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz
180 /*==============================================================================
181 fc2580 frequency setting
183 This function is a generic function which gets called to change LO Frequency
185 of fc2580 in DVB-H mode or L-Band TDMB mode
191 Value of target LO Frequency in 'kHz' unit
194 ==============================================================================*/
195 fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigned int freq_xtal )
197 unsigned int f_diff, f_diff_shifted, n_val, k_val;
198 unsigned int f_vco, r_val, f_comp;
199 unsigned char pre_shift_bits = 4;// number of preshift to prevent overflow in shifting f_diff to f_diff_shifted
200 unsigned char data_0x18;
201 unsigned char data_0x02 = (USE_EXT_CLK<<5)|0x0E;
203 fc2580_band_type band = ( f_lo > 1000000 )? FC2580_L_BAND : ( f_lo > 400000 )? FC2580_UHF_BAND : FC2580_VHF_BAND;
205 fc2580_fci_result_type result = FC2580_FCI_SUCCESS;
207 f_vco = ( band == FC2580_UHF_BAND )? f_lo * 4 : (( band == FC2580_L_BAND )? f_lo * 2 : f_lo * 12);
208 r_val = ( f_vco >= 2*76*freq_xtal )? 1 : ( f_vco >= 76*freq_xtal )? 2 : 4;
209 f_comp = freq_xtal/r_val;
210 n_val = ( f_vco / 2 ) / f_comp;
212 f_diff = f_vco - 2* f_comp * n_val;
213 f_diff_shifted = f_diff << ( 20 - pre_shift_bits );
214 k_val = f_diff_shifted / ( ( 2* f_comp ) >> pre_shift_bits );
216 if( f_diff_shifted - k_val * ( ( 2* f_comp ) >> pre_shift_bits ) >= ( f_comp >> pre_shift_bits ) )
219 if( f_vco >= BORDER_FREQ ) //Select VCO Band
220 data_0x02 = data_0x02 | 0x08; //0x02[3] = 1;
222 data_0x02 = data_0x02 & 0xF7; //0x02[3] = 0;
224 // if( band != curr_band ) {
227 case FC2580_UHF_BAND:
228 data_0x02 = (data_0x02 & 0x3F);
230 result &= fc2580_i2c_write(pTuner, 0x25, 0xF0);
231 result &= fc2580_i2c_write(pTuner, 0x27, 0x77);
232 result &= fc2580_i2c_write(pTuner, 0x28, 0x53);
233 result &= fc2580_i2c_write(pTuner, 0x29, 0x60);
234 result &= fc2580_i2c_write(pTuner, 0x30, 0x09);
235 result &= fc2580_i2c_write(pTuner, 0x50, 0x8C);
236 result &= fc2580_i2c_write(pTuner, 0x53, 0x50);
239 result &= fc2580_i2c_write(pTuner, 0x5F, 0x13);
241 result &= fc2580_i2c_write(pTuner, 0x5F, 0x15);
245 result &= fc2580_i2c_write(pTuner, 0x61, 0x07);
246 result &= fc2580_i2c_write(pTuner, 0x62, 0x06);
247 result &= fc2580_i2c_write(pTuner, 0x67, 0x06);
248 result &= fc2580_i2c_write(pTuner, 0x68, 0x08);
249 result &= fc2580_i2c_write(pTuner, 0x69, 0x10);
250 result &= fc2580_i2c_write(pTuner, 0x6A, 0x12);
252 else if( f_lo < 794000 )
254 result &= fc2580_i2c_write(pTuner, 0x61, 0x03);
255 result &= fc2580_i2c_write(pTuner, 0x62, 0x03);
256 result &= fc2580_i2c_write(pTuner, 0x67, 0x03); //ACI improve
257 result &= fc2580_i2c_write(pTuner, 0x68, 0x05); //ACI improve
258 result &= fc2580_i2c_write(pTuner, 0x69, 0x0C);
259 result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E);
263 result &= fc2580_i2c_write(pTuner, 0x61, 0x07);
264 result &= fc2580_i2c_write(pTuner, 0x62, 0x06);
265 result &= fc2580_i2c_write(pTuner, 0x67, 0x07);
266 result &= fc2580_i2c_write(pTuner, 0x68, 0x09);
267 result &= fc2580_i2c_write(pTuner, 0x69, 0x10);
268 result &= fc2580_i2c_write(pTuner, 0x6A, 0x12);
271 result &= fc2580_i2c_write(pTuner, 0x63, 0x15);
273 result &= fc2580_i2c_write(pTuner, 0x6B, 0x0B);
274 result &= fc2580_i2c_write(pTuner, 0x6C, 0x0C);
275 result &= fc2580_i2c_write(pTuner, 0x6D, 0x78);
276 result &= fc2580_i2c_write(pTuner, 0x6E, 0x32);
277 result &= fc2580_i2c_write(pTuner, 0x6F, 0x14);
278 result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz
280 case FC2580_VHF_BAND:
281 data_0x02 = (data_0x02 & 0x3F) | 0x80;
282 result &= fc2580_i2c_write(pTuner, 0x27, 0x77);
283 result &= fc2580_i2c_write(pTuner, 0x28, 0x33);
284 result &= fc2580_i2c_write(pTuner, 0x29, 0x40);
285 result &= fc2580_i2c_write(pTuner, 0x30, 0x09);
286 result &= fc2580_i2c_write(pTuner, 0x50, 0x8C);
287 result &= fc2580_i2c_write(pTuner, 0x53, 0x50);
288 result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F);
289 result &= fc2580_i2c_write(pTuner, 0x61, 0x07);
290 result &= fc2580_i2c_write(pTuner, 0x62, 0x00);
291 result &= fc2580_i2c_write(pTuner, 0x63, 0x15);
292 result &= fc2580_i2c_write(pTuner, 0x67, 0x03);
293 result &= fc2580_i2c_write(pTuner, 0x68, 0x05);
294 result &= fc2580_i2c_write(pTuner, 0x69, 0x10);
295 result &= fc2580_i2c_write(pTuner, 0x6A, 0x12);
296 result &= fc2580_i2c_write(pTuner, 0x6B, 0x08);
297 result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A);
298 result &= fc2580_i2c_write(pTuner, 0x6D, 0x78);
299 result &= fc2580_i2c_write(pTuner, 0x6E, 0x32);
300 result &= fc2580_i2c_write(pTuner, 0x6F, 0x54);
301 result &= fc2580_set_filter(pTuner, 7, freq_xtal); //BW = 6.8MHz
304 data_0x02 = (data_0x02 & 0x3F) | 0x40;
305 result &= fc2580_i2c_write(pTuner, 0x2B, 0x70);
306 result &= fc2580_i2c_write(pTuner, 0x2C, 0x37);
307 result &= fc2580_i2c_write(pTuner, 0x2D, 0xE7);
308 result &= fc2580_i2c_write(pTuner, 0x30, 0x09);
309 result &= fc2580_i2c_write(pTuner, 0x44, 0x20);
310 result &= fc2580_i2c_write(pTuner, 0x50, 0x8C);
311 result &= fc2580_i2c_write(pTuner, 0x53, 0x50);
312 result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F);
313 result &= fc2580_i2c_write(pTuner, 0x61, 0x0F);
314 result &= fc2580_i2c_write(pTuner, 0x62, 0x00);
315 result &= fc2580_i2c_write(pTuner, 0x63, 0x13);
316 result &= fc2580_i2c_write(pTuner, 0x67, 0x00);
317 result &= fc2580_i2c_write(pTuner, 0x68, 0x02);
318 result &= fc2580_i2c_write(pTuner, 0x69, 0x0C);
319 result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E);
320 result &= fc2580_i2c_write(pTuner, 0x6B, 0x08);
321 result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A);
322 result &= fc2580_i2c_write(pTuner, 0x6D, 0xA0);
323 result &= fc2580_i2c_write(pTuner, 0x6E, 0x50);
324 result &= fc2580_i2c_write(pTuner, 0x6F, 0x14);
325 result &= fc2580_set_filter(pTuner, 1, freq_xtal); //BW = 1.53MHz
333 //A command about AGC clock's pre-divide ratio
334 if( freq_xtal >= 28000 )
335 result &= fc2580_i2c_write(pTuner, 0x4B, 0x22 );
337 //Commands about VCO Band and PLL setting.
338 result &= fc2580_i2c_write(pTuner, 0x02, data_0x02);
339 data_0x18 = ( ( r_val == 1 )? 0x00 : ( ( r_val == 2 )? 0x10 : 0x20 ) ) + (unsigned char)(k_val >> 16);
340 result &= fc2580_i2c_write(pTuner, 0x18, data_0x18); //Load 'R' value and high part of 'K' values
341 result &= fc2580_i2c_write(pTuner, 0x1A, (unsigned char)( k_val >> 8 ) ); //Load middle part of 'K' value
342 result &= fc2580_i2c_write(pTuner, 0x1B, (unsigned char)( k_val ) ); //Load lower part of 'K' value
343 result &= fc2580_i2c_write(pTuner, 0x1C, (unsigned char)( n_val ) ); //Load 'N' value
345 //A command about UHF LNA Load Cap
346 if( band == FC2580_UHF_BAND )
347 result &= fc2580_i2c_write(pTuner, 0x2D, ( f_lo <= (unsigned int)794000 )? 0x9F : 0x8F ); //LNA_OUT_CAP
354 /*==============================================================================
355 fc2580 filter BW setting
357 This function is a generic function which gets called to change Bandwidth
359 frequency of fc2580's channel selection filter
366 6 : 6MHz (Bandwidth 6MHz)
367 7 : 6.8MHz (Bandwidth 7MHz)
368 8 : 7.8MHz (Bandwidth 8MHz)
371 ==============================================================================*/
372 fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal )
374 unsigned char cal_mon = 0, i;
375 fc2580_fci_result_type result = FC2580_FCI_SUCCESS;
379 result &= fc2580_i2c_write(pTuner, 0x36, 0x1C);
380 result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4151*freq_xtal/1000000) );
381 result &= fc2580_i2c_write(pTuner, 0x39, 0x00);
382 result &= fc2580_i2c_write(pTuner, 0x2E, 0x09);
386 result &= fc2580_i2c_write(pTuner, 0x36, 0x18);
387 result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4400*freq_xtal/1000000) );
388 result &= fc2580_i2c_write(pTuner, 0x39, 0x00);
389 result &= fc2580_i2c_write(pTuner, 0x2E, 0x09);
391 else if(filter_bw == 7)
393 result &= fc2580_i2c_write(pTuner, 0x36, 0x18);
394 result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3910*freq_xtal/1000000) );
395 result &= fc2580_i2c_write(pTuner, 0x39, 0x80);
396 result &= fc2580_i2c_write(pTuner, 0x2E, 0x09);
398 else if(filter_bw == 8)
400 result &= fc2580_i2c_write(pTuner, 0x36, 0x18);
401 result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3300*freq_xtal/1000000) );
402 result &= fc2580_i2c_write(pTuner, 0x39, 0x80);
403 result &= fc2580_i2c_write(pTuner, 0x2E, 0x09);
409 fc2580_wait_msec(pTuner, 5);//wait 5ms
410 result &= fc2580_i2c_read(pTuner, 0x2F, &cal_mon);
411 if( (cal_mon & 0xC0) != 0xC0)
413 result &= fc2580_i2c_write(pTuner, 0x2E, 0x01);
414 result &= fc2580_i2c_write(pTuner, 0x2E, 0x09);
420 result &= fc2580_i2c_write(pTuner, 0x2E, 0x01);
425 /*==============================================================================
428 This function is a generic function which returns fc2580's
437 rssi : estimated input power.
439 ==============================================================================*/
440 //int fc2580_get_rssi(void) {
442 // unsigned char s_lna, s_rfvga, s_cfs, s_ifvga;
443 // int ofs_lna, ofs_rfvga, ofs_csf, ofs_ifvga, rssi;
445 // fc2580_i2c_read(0x71, &s_lna );
446 // fc2580_i2c_read(0x72, &s_rfvga );
447 // fc2580_i2c_read(0x73, &s_cfs );
448 // fc2580_i2c_read(0x74, &s_ifvga );
452 // (curr_band==FC2580_UHF_BAND)?
456 // (s_lna==3)? -22 : -30 :
457 // (curr_band==FC2580_VHF_BAND)?
461 // (s_lna==3)? -24 : -32 :
462 // (curr_band==FC2580_L_BAND)?
466 // (s_lna==3)? -16 : -34 :
467 // 0;//FC2580_NO_BAND
468 // ofs_rfvga = -s_rfvga+((s_rfvga>=11)? 1 : 0) + ((s_rfvga>=18)? 1 : 0);
469 // ofs_csf = -6*s_cfs;
470 // ofs_ifvga = s_ifvga/4;
472 // return rssi = ofs_lna+ofs_rfvga+ofs_csf+ofs_ifvga+OFS_RSSI;
476 /*==============================================================================
477 fc2580 Xtal frequency Setting
479 This function is a generic function which sets
481 the frequency of xtal.
486 frequency value of internal(external) Xtal(clock) in kHz unit.
488 ==============================================================================*/
489 //void fc2580_set_freq_xtal(unsigned int frequency) {
491 // freq_xtal = frequency;