From 86c34428aabebc79ac0de7f1194e6b755a6fa213 Mon Sep 17 00:00:00 2001 From: Steve Markgraf Date: Fri, 4 May 2012 23:02:29 +0200 Subject: [PATCH] use new E4000 tuner driver, allow manual gain Many thanks to Hoernchen for making the driver work properly and adding manual gain! Signed-off-by: Steve Markgraf --- include/Makefile.am | 2 +- include/reg_field.h | 60 ++ include/rtl-sdr.h | 2 + include/tuner_e4000.h | 129 --- include/tuner_e4k.h | 219 +++++ src/CMakeLists.txt | 4 +- src/Makefile.am | 2 +- src/rtl-sdr.c | 98 +- src/rtl_tcp.c | 7 +- src/tuner_e4000.c | 2067 ----------------------------------------- src/tuner_e4k.c | 955 +++++++++++++++++++ 11 files changed, 1328 insertions(+), 2217 deletions(-) create mode 100644 include/reg_field.h delete mode 100644 include/tuner_e4000.h create mode 100644 include/tuner_e4k.h delete mode 100644 src/tuner_e4000.c create mode 100644 src/tuner_e4k.c diff --git a/include/Makefile.am b/include/Makefile.am index 0caf762..1fcc60c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,5 @@ rtlsdr_HEADERS = rtl-sdr.h rtl-sdr_export.h -noinst_HEADERS = rtlsdr_i2c.h tuner_e4000.h tuner_fc0012.h tuner_fc0013.h tuner_fc2580.h +noinst_HEADERS = rtlsdr_i2c.h tuner_e4k.h tuner_fc0012.h tuner_fc0013.h tuner_fc2580.h rtlsdrdir = $(includedir) diff --git a/include/reg_field.h b/include/reg_field.h new file mode 100644 index 0000000..18a6922 --- /dev/null +++ b/include/reg_field.h @@ -0,0 +1,60 @@ +#ifndef _REG_FIELD_H +#define _REG_FIELD_H + +#include +#include + +enum cmd_op { + CMD_OP_GET = (1 << 0), + CMD_OP_SET = (1 << 1), + CMD_OP_EXEC = (1 << 2), +}; + +enum pstate { + ST_IN_CMD, + ST_IN_ARG, +}; + +struct strbuf { + uint8_t idx; + char buf[32]; +}; + +struct cmd_state { + struct strbuf cmd; + struct strbuf arg; + enum pstate state; + void (*out)(const char *format, va_list ap); +}; + +struct cmd { + const char *cmd; + uint32_t ops; + int (*cb)(struct cmd_state *cs, enum cmd_op op, const char *cmd, + int argc, char **argv); + const char *help; +}; + +/* structure describing a field in a register */ +struct reg_field { + uint8_t reg; + uint8_t shift; + uint8_t width; +}; + +struct reg_field_ops { + const struct reg_field *fields; + const char **field_names; + uint32_t num_fields; + void *data; + int (*write_cb)(void *data, uint32_t reg, uint32_t val); + uint32_t (*read_cb)(void *data, uint32_t reg); +}; + +uint32_t reg_field_read(struct reg_field_ops *ops, struct reg_field *field); +int reg_field_write(struct reg_field_ops *ops, struct reg_field *field, uint32_t val); +int reg_field_cmd(struct cmd_state *cs, enum cmd_op op, + const char *cmd, int argc, char **argv, + struct reg_field_ops *ops); + +#endif diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index e5b3f75..9688bda 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -87,6 +87,8 @@ RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); +RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); + /* this will select the baseband filters according to the requested sample rate */ RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); diff --git a/include/tuner_e4000.h b/include/tuner_e4000.h deleted file mode 100644 index 5aa71f5..0000000 --- a/include/tuner_e4000.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef __TUNER_E4000_H -#define __TUNER_E4000_H - -// Definition (implemeted for E4000) -#define E4000_1_SUCCESS 1 -#define E4000_1_FAIL 0 -#define E4000_I2C_SUCCESS 1 -#define E4000_I2C_FAIL 0 - -#define E4K_I2C_ADDR 0xc8 -#define E4K_CHECK_ADDR 0x02 -#define E4K_CHECK_VAL 0x40 - -// Function (implemeted for E4000) -int -I2CReadByte(void *pTuner, - unsigned char NoUse, - unsigned char RegAddr, - unsigned char *pReadingByte - ); - -int -I2CWriteByte( - void *pTuner, - unsigned char NoUse, - unsigned char RegAddr, - unsigned char WritingByte - ); - -int -I2CWriteArray(void *pTuner, - unsigned char NoUse, - unsigned char RegStartAddr, - unsigned char ByteNum, - unsigned char *pWritingBytes - ); - - - -// Functions (from E4000 source code) -int tunerreset (void *pTuner); -int Tunerclock(void *pTuner); -int Qpeak(void *pTuner); -int DCoffloop(void *pTuner); -int GainControlinit(void *pTuner); - -int Gainmanual(void *pTuner); -int E4000_gain_freq(void *pTuner, int frequency); -int PLL(void *pTuner, int Ref_clk, int Freq); -int LNAfilter(void *pTuner, int Freq); -int IFfilter(void *pTuner, int bandwidth, int Ref_clk); -int freqband(void *pTuner, int Freq); -int DCoffLUT(void *pTuner); -int GainControlauto(void *pTuner); - -int E4000_sensitivity(void *pTuner, int Freq, int bandwidth); -int E4000_linearity(void *pTuner, int Freq, int bandwidth); -int E4000_high_linearity(void *pTuner); -int E4000_nominal(void *pTuner, int Freq, int bandwidth); - - -// The following context is E4000 tuner API source code - -// Definitions - -// Bandwidth in Hz -enum E4000_BANDWIDTH_HZ -{ - E4000_BANDWIDTH_6000000HZ = 6000000, - E4000_BANDWIDTH_7000000HZ = 7000000, - E4000_BANDWIDTH_8000000HZ = 8000000, -}; - - -// Manipulaing functions -void -e4000_GetTunerType( - void *pTuner, - int *pTunerType - ); - -void -e4000_GetDeviceAddr( - void *pTuner, - unsigned char *pDeviceAddr - ); - -int -e4000_Initialize( - void *pTuner - ); - -int -e4000_SetRfFreqHz( - void *pTuner, - unsigned long RfFreqHz - ); - -int -e4000_GetRfFreqHz( - void *pTuner, - unsigned long *pRfFreqHz - ); - - - - - -// Extra manipulaing functions -int -e4000_GetRegByte( - void *pTuner, - unsigned char RegAddr, - unsigned char *pReadingByte - ); - -int -e4000_SetBandwidthHz( - void *pTuner, - unsigned long BandwidthHz - ); - -int -e4000_GetBandwidthHz( - void *pTuner, - unsigned long *pBandwidthHz - ); - -#endif diff --git a/include/tuner_e4k.h b/include/tuner_e4k.h new file mode 100644 index 0000000..969ef7c --- /dev/null +++ b/include/tuner_e4k.h @@ -0,0 +1,219 @@ +#ifndef _E4K_TUNER_H +#define _E4K_TUNER_H + +/* (C) 2011-2012 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define E4K_I2C_ADDR 0xc8 +#define E4K_CHECK_ADDR 0x02 +#define E4K_CHECK_VAL 0x40 + +enum e4k_reg { + E4K_REG_MASTER1 = 0x00, + E4K_REG_MASTER2 = 0x01, + E4K_REG_MASTER3 = 0x02, + E4K_REG_MASTER4 = 0x03, + E4K_REG_MASTER5 = 0x04, + E4K_REG_CLK_INP = 0x05, + E4K_REG_REF_CLK = 0x06, + E4K_REG_SYNTH1 = 0x07, + E4K_REG_SYNTH2 = 0x08, + E4K_REG_SYNTH3 = 0x09, + E4K_REG_SYNTH4 = 0x0a, + E4K_REG_SYNTH5 = 0x0b, + E4K_REG_SYNTH6 = 0x0c, + E4K_REG_SYNTH7 = 0x0d, + E4K_REG_SYNTH8 = 0x0e, + E4K_REG_SYNTH9 = 0x0f, + E4K_REG_FILT1 = 0x10, + E4K_REG_FILT2 = 0x11, + E4K_REG_FILT3 = 0x12, + // gap + E4K_REG_GAIN1 = 0x14, + E4K_REG_GAIN2 = 0x15, + E4K_REG_GAIN3 = 0x16, + E4K_REG_GAIN4 = 0x17, + // gap + E4K_REG_AGC1 = 0x1a, + E4K_REG_AGC2 = 0x1b, + E4K_REG_AGC3 = 0x1c, + E4K_REG_AGC4 = 0x1d, + E4K_REG_AGC5 = 0x1e, + E4K_REG_AGC6 = 0x1f, + E4K_REG_AGC7 = 0x20, + E4K_REG_AGC8 = 0x21, + // gap + E4K_REG_AGC11 = 0x24, + E4K_REG_AGC12 = 0x25, + // gap + E4K_REG_DC1 = 0x29, + E4K_REG_DC2 = 0x2a, + E4K_REG_DC3 = 0x2b, + E4K_REG_DC4 = 0x2c, + E4K_REG_DC5 = 0x2d, + E4K_REG_DC6 = 0x2e, + E4K_REG_DC7 = 0x2f, + E4K_REG_DC8 = 0x30, + // gap + E4K_REG_QLUT0 = 0x50, + E4K_REG_QLUT1 = 0x51, + E4K_REG_QLUT2 = 0x52, + E4K_REG_QLUT3 = 0x53, + // gap + E4K_REG_ILUT0 = 0x60, + E4K_REG_ILUT1 = 0x61, + E4K_REG_ILUT2 = 0x62, + E4K_REG_ILUT3 = 0x63, + // gap + E4K_REG_DCTIME1 = 0x70, + E4K_REG_DCTIME2 = 0x71, + E4K_REG_DCTIME3 = 0x72, + E4K_REG_DCTIME4 = 0x73, + E4K_REG_PWM1 = 0x74, + E4K_REG_PWM2 = 0x75, + E4K_REG_PWM3 = 0x76, + E4K_REG_PWM4 = 0x77, + E4K_REG_BIAS = 0x78, + E4K_REG_CLKOUT_PWDN = 0x7a, + E4K_REG_CHFILT_CALIB = 0x7b, + E4K_REG_I2C_REG_ADDR = 0x7d, + // FIXME +}; + +#define E4K_MASTER1_RESET (1 << 0) +#define E4K_MASTER1_NORM_STBY (1 << 1) +#define E4K_MASTER1_POR_DET (1 << 2) + +#define E4K_SYNTH1_PLL_LOCK (1 << 0) +#define E4K_SYNTH1_BAND_SHIF 1 + +#define E4K_SYNTH7_3PHASE_EN (1 << 3) + +#define E4K_SYNTH8_VCOCAL_UPD (1 << 2) + +#define E4K_FILT3_DISABLE (1 << 5) + +#define E4K_AGC1_LIN_MODE (1 << 4) +#define E4K_AGC1_LNA_UPDATE (1 << 5) +#define E4K_AGC1_LNA_G_LOW (1 << 6) +#define E4K_AGC1_LNA_G_HIGH (1 << 7) + +#define E4K_AGC6_LNA_CAL_REQ (1 << 4) + +#define E4K_AGC7_MIX_GAIN_AUTO (1 << 0) +#define E4K_AGC7_GAIN_STEP_5dB (1 << 5) + +#define E4K_AGC8_SENS_LIN_AUTO (1 << 0) + +#define E4K_AGC11_LNA_GAIN_ENH (1 << 0) + +#define E4K_DC1_CAL_REQ (1 << 0) + +#define E4K_DC5_I_LUT_EN (1 << 0) +#define E4K_DC5_Q_LUT_EN (1 << 1) +#define E4K_DC5_RANGE_DET_EN (1 << 2) +#define E4K_DC5_RANGE_EN (1 << 3) +#define E4K_DC5_TIMEVAR_EN (1 << 4) + +#define E4K_CLKOUT_DISABLE 0x96 + +#define E4K_CHFCALIB_CMD (1 << 0) + +#define E4K_AGC1_MOD_MASK 0xF + +enum e4k_agc_mode { + E4K_AGC_MOD_SERIAL = 0x0, + E4K_AGC_MOD_IF_PWM_LNA_SERIAL = 0x1, + E4K_AGC_MOD_IF_PWM_LNA_AUTONL = 0x2, + E4K_AGC_MOD_IF_PWM_LNA_SUPERV = 0x3, + E4K_AGC_MOD_IF_SERIAL_LNA_PWM = 0x4, + E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, + E4K_AGC_MOD_IF_DIG_LNA_SERIAL = 0x6, + E4K_AGC_MOD_IF_DIG_LNA_AUTON = 0x7, + E4K_AGC_MOD_IF_DIG_LNA_SUPERV = 0x8, + E4K_AGC_MOD_IF_SERIAL_LNA_AUTON = 0x9, + E4K_AGC_MOD_IF_SERIAL_LNA_SUPERV = 0xa, +}; + +enum e4k_band { + E4K_BAND_VHF2 = 0, + E4K_BAND_VHF3 = 1, + E4K_BAND_UHF = 2, + E4K_BAND_L = 3, +}; + +enum e4k_mixer_filter_bw { + E4K_F_MIX_BW_27M = 0, + E4K_F_MIX_BW_4M6 = 8, + E4K_F_MIX_BW_4M2 = 9, + E4K_F_MIX_BW_3M8 = 10, + E4K_F_MIX_BW_3M4 = 11, + E4K_F_MIX_BW_3M = 12, + E4K_F_MIX_BW_2M7 = 13, + E4K_F_MIX_BW_2M3 = 14, + E4K_F_MIX_BW_1M9 = 15, +}; + +enum e4k_if_filter { + E4K_IF_FILTER_MIX, + E4K_IF_FILTER_CHAN, + E4K_IF_FILTER_RC +}; +struct e4k_pll_params { + uint32_t fosc; + uint32_t intended_flo; + uint32_t flo; + uint16_t x; + uint8_t z; + uint8_t r; + uint8_t r_idx; + uint8_t threephase; +}; + +struct e4k_state { + void *i2c_dev; + uint8_t i2c_addr; + enum e4k_band band; + struct e4k_pll_params vco; + void *rtl_dev; +}; + +int e4k_init(struct e4k_state *e4k); +int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value); +int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value); +int e4k_commonmode_set(struct e4k_state *e4k, int8_t value); +int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq); +int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p); +int e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo); +int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter); +int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, + uint32_t bandwidth); +int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on); +int e4k_rf_filter_set(struct e4k_state *e4k); + +int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val); +uint8_t e4k_reg_read(struct e4k_state *e4k, uint8_t reg); + +int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange); +int e4k_dc_offset_calibrate(struct e4k_state *e4k); +int e4k_dc_offset_gen_table(struct e4k_state *e4k); + +int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain); +int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual); +int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain); +#endif /* _E4K_TUNER_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ce6f6b..6d23607 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,7 @@ ######################################################################## add_library(rtlsdr_shared SHARED rtl-sdr.c - tuner_e4000.c + tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c @@ -37,7 +37,7 @@ set_target_properties(rtlsdr_shared PROPERTIES OUTPUT_NAME rtlsdr) add_library(rtlsdr_static STATIC rtl-sdr.c - tuner_e4000.c + tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c diff --git a/src/Makefile.am b/src/Makefile.am index f9023cd..f8cc5d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ AM_CFLAGS = -fPIC -Wall lib_LTLIBRARIES = librtlsdr.la -librtlsdr_la_SOURCES = rtl-sdr.c tuner_e4000.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c +librtlsdr_la_SOURCES = rtl-sdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c librtlsdr_la_LDFALGS = -version-info $(LIBVERSION) bin_PROGRAMS = rtl_sdr rtl_tcp diff --git a/src/rtl-sdr.c b/src/rtl-sdr.c index 46bf84b..6910694 100644 --- a/src/rtl-sdr.c +++ b/src/rtl-sdr.c @@ -25,6 +25,7 @@ #include #ifndef _WIN32 #include +#define min(a, b) (((a) < (b)) ? (a) : (b)) #endif #include @@ -40,7 +41,7 @@ #endif #include "rtl-sdr.h" -#include "tuner_e4000.h" +#include "tuner_e4k.h" #include "tuner_fc0012.h" #include "tuner_fc0013.h" #include "tuner_fc2580.h" @@ -52,6 +53,7 @@ typedef struct rtlsdr_tuner { int (*set_freq)(void *, uint32_t freq /* Hz */); int (*set_bw)(void *, int bw /* Hz */); int (*set_gain)(void *, int gain /* dB */); + int (*set_gain_mode)(void *, int manual); } rtlsdr_tuner_t; enum rtlsdr_async_status { @@ -79,20 +81,48 @@ struct rtlsdr_dev { uint32_t freq; /* Hz */ int corr; /* ppm */ int gain; /* dB */ + struct e4k_state e4k_s; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); /* generic tuner interface functions, shall be moved to the tuner implementations */ -int e4k_init(void *dev) { return e4000_Initialize(dev); } -int e4k_exit(void *dev) { return 0; } -int e4k_set_freq(void *dev, uint32_t freq) { - return e4000_SetRfFreqHz(dev, freq); +int e4000_init(void *dev) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + devt->e4k_s.i2c_addr = E4K_I2C_ADDR; + devt->e4k_s.vco.fosc = devt->tun_xtal; + devt->e4k_s.rtl_dev = dev; + return e4k_init(&devt->e4k_s); } -int e4k_set_bw(void *dev, int bw) { - return e4000_SetBandwidthHz(dev, 4000000); +int e4000_exit(void *dev) { return 0; } +int e4000_set_freq(void *dev, uint32_t freq) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return e4k_tune_freq(&devt->e4k_s, freq); +} +int e4000_set_bw(void *dev, int bw) { + return 0; +} +int e4000_set_gain(void *dev, int gain) { + int rc; + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + int mixgain = (gain > 340) ? 12 : 4; + int enhgain = (gain - 420); + if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - 40)) == -EINVAL) + return -1; + if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) + return -1; + if(enhgain >= 0) + if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) + return -1; + + return 0; +} + +int e4000_set_gain_mode(void *dev, int manual) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + e4k_enable_manual_gain(&devt->e4k_s, manual); + return 0; } -int e4k_set_gain(void *dev, int gain) { return 0; } int fc0012_init(void *dev) { return FC0012_Open(dev); } int fc0012_exit(void *dev) { return 0; } @@ -105,6 +135,7 @@ int fc0012_set_bw(void *dev, int bw) { return FC0012_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); } int fc0012_set_gain(void *dev, int gain) { return 0; } +int fc0012_set_gain_mode(void *dev, int manual) { return 0; } int fc0013_init(void *dev) { return FC0013_Open(dev); } int fc0013_exit(void *dev) { return 0; } @@ -115,6 +146,7 @@ int fc0013_set_bw(void *dev, int bw) { return FC0013_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); } int fc0013_set_gain(void *dev, int gain) { return 0; } +int fc0013_set_gain_mode(void *dev, int manual) { return 0; } int fc2580_init(void *dev) { return fc2580_Initialize(dev); } int fc2580_exit(void *dev) { return 0; } @@ -125,6 +157,7 @@ int fc2580_set_bw(void *dev, int bw) { return fc2580_SetBandwidthMode(dev, 1); } int fc2580_set_gain(void *dev, int gain) { return 0; } +int fc2580_set_gain_mode(void *dev, int manual) { return 0; } enum rtlsdr_tuners { RTLSDR_TUNER_E4000, @@ -135,20 +168,24 @@ enum rtlsdr_tuners { static rtlsdr_tuner_t tuners[] = { { - e4k_init, e4k_exit, - e4k_set_freq, e4k_set_bw, e4k_set_gain + e4000_init, e4000_exit, + e4000_set_freq, e4000_set_bw, e4000_set_gain, + e4000_set_gain_mode }, { fc0012_init, fc0012_exit, - fc0012_set_freq, fc0012_set_bw, fc0012_set_gain + fc0012_set_freq, fc0012_set_bw, fc0012_set_gain, + fc0012_set_gain_mode }, { fc0013_init, fc0013_exit, - fc0013_set_freq, fc0013_set_bw, fc0013_set_gain + fc0013_set_freq, fc0013_set_bw, fc0013_set_gain, + fc0013_set_gain_mode }, { fc2580_init, fc2580_exit, - _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain + _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, + fc2580_set_gain_mode }, }; @@ -277,6 +314,16 @@ uint8_t rtlsdr_i2c_read_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg) return data; } +/* TODO clean this up again */ +int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) +{ + return rtlsdr_i2c_write_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg, val);} + +uint8_t e4k_reg_read(struct e4k_state *e4k, uint8_t reg) +{ + return rtlsdr_i2c_read_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg); +} + int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) { uint16_t addr = i2c_addr; @@ -594,8 +641,11 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) if (!dev || !dev->tuner) return -1; - if (dev->tuner->set_gain) + if (dev->tuner->set_gain) { + rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_gain((void *)dev, gain); + rtlsdr_set_i2c_repeater(dev, 0); + } if (!r) dev->gain = gain; @@ -611,6 +661,24 @@ int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) return dev->gain; } +int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_gain_mode) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_gain_mode((void *)dev, mode); + rtlsdr_set_i2c_repeater(dev, 0); + } + + return r; +} + + + int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) { uint16_t tmp; @@ -862,8 +930,6 @@ err: int rtlsdr_close(rtlsdr_dev_t *dev) { - int i; - if (!dev) return -1; diff --git a/src/rtl_tcp.c b/src/rtl_tcp.c index f1f05ca..89019c0 100644 --- a/src/rtl_tcp.c +++ b/src/rtl_tcp.c @@ -32,6 +32,8 @@ #include #include #include +#else +#include #endif #include @@ -278,6 +280,9 @@ static void *command_worker(void *arg) printf("set freq %d\n", cmd.param); rtlsdr_set_center_freq(dev, cmd.param); break; + case 0x04: + rtlsdr_set_tuner_gain(dev, cmd.param); + break; default: break; } @@ -293,7 +298,7 @@ int main(int argc, char **argv) uint32_t frequency = 0, samp_rate = 2048000; struct sockaddr_in local, remote; int device_count; - uint32_t dev_index = 0, gain = 5; + uint32_t dev_index = 0, gain = -10; struct llist *curelem,*prev; pthread_attr_t attr; void *status; diff --git a/src/tuner_e4000.c b/src/tuner_e4000.c deleted file mode 100644 index 5772331..0000000 --- a/src/tuner_e4000.c +++ /dev/null @@ -1,2067 +0,0 @@ -/* - * Elonics E4000 tuner driver, taken from the kernel driver that can be found - * on http://linux.terratec.de/tv_en.html - * - * This driver is a mess, and should be replaced by the osmo-sdr E4000 driver - * - */ - -#include - -#include "rtlsdr_i2c.h" -#include "tuner_e4000.h" - -#define FUNCTION_ERROR 1 -#define FUNCTION_SUCCESS 0 -#define NO_USE 0 -#define LEN_2_BYTE 2 -#define I2C_BUFFER_LEN 128 -#define YES 1 -#define NO 0 -#define CRYSTAL_FREQ 28800000 - -/* glue functions to rtl-sdr code */ -int -I2CReadByte(void *pTuner, - unsigned char NoUse, - unsigned char RegAddr, - unsigned char *pReadingByte - ) -{ - uint8_t data = RegAddr; - - if (rtlsdr_i2c_write_fn(pTuner, E4K_I2C_ADDR, &data, 1) < 0) - return E4000_I2C_FAIL; - - if (rtlsdr_i2c_read_fn(pTuner, E4K_I2C_ADDR, &data, 1) < 0) - return E4000_I2C_FAIL; - - *pReadingByte = data; - - return E4000_I2C_SUCCESS; -} - -int -I2CWriteByte(void *pTuner, - unsigned char NoUse, - unsigned char RegAddr, - unsigned char WritingByte - ) -{ - uint8_t data[2]; - - data[0] = RegAddr; - data[1] = WritingByte; - - if (rtlsdr_i2c_write_fn(pTuner, E4K_I2C_ADDR, data, 2) < 0) - return E4000_I2C_FAIL; - - return E4000_I2C_SUCCESS; -} - -int -I2CWriteArray(void *pTuner, - unsigned char NoUse, - unsigned char RegStartAddr, - unsigned char ByteNum, - unsigned char *pWritingBytes - ) -{ - unsigned int i; - uint8_t WritingBuffer[I2C_BUFFER_LEN]; - - WritingBuffer[0] = RegStartAddr; - - for(i = 0; i < ByteNum; i++) - WritingBuffer[1 + i] = pWritingBytes[i]; - - if (rtlsdr_i2c_write_fn(pTuner, E4K_I2C_ADDR, WritingBuffer, ByteNum + 1) < 0) - return E4000_I2C_FAIL; - - return E4000_I2C_SUCCESS; -} - -/** - -@see TUNER_FP_INITIALIZE - -*/ -int -e4000_Initialize(void *pTuner - ) -{ - - // Initialize tuner. - // Note: Call E4000 source code functions. - if(tunerreset(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(Tunerclock(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(Qpeak(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; -#if 0 - if(DCoffloop(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; -#endif - if(GainControlinit(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - - return FUNCTION_SUCCESS; - - -error_status_execute_function: - return FUNCTION_ERROR; -} - -/** - -@see TUNER_FP_SET_RF_FREQ_HZ - -*/ -int -e4000_SetRfFreqHz( - void *pTuner, - unsigned long RfFreqHz - ) -{ -// E4000_EXTRA_MODULE *pExtra; - - int RfFreqKhz; - int CrystalFreqKhz; - - int CrystalFreqHz = rtlsdr_get_tuner_clock(pTuner); - - // Set tuner RF frequency in KHz. - // Note: 1. RfFreqKhz = round(RfFreqHz / 1000) - // CrystalFreqKhz = round(CrystalFreqHz / 1000) - // 2. Call E4000 source code functions. - RfFreqKhz = (int)((RfFreqHz + 500) / 1000); - CrystalFreqKhz = (int)((CrystalFreqHz + 500) / 1000); - - if(Gainmanual(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(E4000_gain_freq(pTuner, RfFreqKhz) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(PLL(pTuner, CrystalFreqKhz, RfFreqKhz) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(LNAfilter(pTuner, RfFreqKhz) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(freqband(pTuner, RfFreqKhz) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(DCoffLUT(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - if(GainControlauto(pTuner) != E4000_1_SUCCESS) - goto error_status_execute_function; - - return FUNCTION_SUCCESS; - - -error_status_execute_function: - return FUNCTION_ERROR; -} - -/** - -@brief Set E4000 tuner bandwidth. - -*/ -int -e4000_SetBandwidthHz( - void *pTuner, - unsigned long BandwidthHz - ) -{ -// E4000_EXTRA_MODULE *pExtra; - - int BandwidthKhz; - int CrystalFreqKhz; - - int CrystalFreqHz = rtlsdr_get_tuner_clock(pTuner); - - - // Get tuner extra module. -// pExtra = &(pTuner->Extra.E4000); - - - // Set tuner bandwidth Hz. - // Note: 1. BandwidthKhz = round(BandwidthHz / 1000) - // CrystalFreqKhz = round(CrystalFreqHz / 1000) - // 2. Call E4000 source code functions. - BandwidthKhz = (int)((BandwidthHz + 500) / 1000); - CrystalFreqKhz = (int)((CrystalFreqHz + 500) / 1000); - - if(IFfilter(pTuner, BandwidthKhz, CrystalFreqKhz) != E4000_1_SUCCESS) - goto error_status_execute_function; - - - return FUNCTION_SUCCESS; - -error_status_execute_function: - return FUNCTION_ERROR; -} - - -// The following context is source code provided by Elonics. - -// Elonics source code - E4000_API_rev2_04_realtek.cpp - - -//****************************************************************************/ -// -// Filename E4000_initialisation.c -// Revision 2.04 -// -// Description: -// Initialisation script for the Elonics E4000 revC tuner -// -// Copyright (c) Elonics Ltd -// -// Any software supplied free of charge for use with elonics -// evaluation kits is supplied without warranty and for -// evaluation purposes only. Incorporation of any of this -// code into products for open sale is permitted but only at -// the user's own risk. Elonics accepts no liability for the -// integrity of this software whatsoever. -// -// -//****************************************************************************/ -//#include -//#include -// -// User defined variable definitions -// -/* -int Ref_clk = 26000; // Reference clock frequency(kHz). -int Freq = 590000; // RF Frequency (kHz) -int bandwidth = 8000; //RF channel bandwith (kHz) -*/ -// -// API defined variable definitions -//int VCO_freq; -//unsigned char writearray[5]; -//unsigned char read1[1]; -//int status; -// -// -// function definitions -// -/* -int tunerreset (); -int Tunerclock(); -int filtercal(); -int Qpeak(); -int PLL(int Ref_clk, int Freq); -int LNAfilter(int Freq); -int IFfilter(int bandwidth, int Ref_clk); -int freqband(int Freq); -int DCoffLUT(); -int DCoffloop(); -int commonmode(); -int GainControlinit(); -*/ -// -//**************************************************************************** -// --- Public functions ------------------------------------------------------ -/****************************************************************************\ -* Function: tunerreset -* -* Detailed Description: -* The function resets the E4000 tuner. (Register 0x00). -* -\****************************************************************************/ - -int tunerreset(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - writearray[0] = 64; - // For dummy I2C command, don't check executing status. - status=I2CWriteByte (pTuner, 200,2,writearray[0]); - status=I2CWriteByte (pTuner, 200,2,writearray[0]); - //printf("\nRegister 0=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteByte (pTuner, 200,9,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteByte (pTuner, 200,5,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 7; - status=I2CWriteByte (pTuner, 200,0,writearray[0]); - //printf("\nRegister 0=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: Tunerclock -* -* Detailed Description: -* The function configures the E4000 clock. (Register 0x06, 0x7a). -* Function disables the clock - values can be modified to enable if required. -\****************************************************************************/ - -int Tunerclock(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - writearray[0] = 0; - status=I2CWriteByte(pTuner, 200,6,writearray[0]); - //printf("\nRegister 6=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 150; - status=I2CWriteByte(pTuner, 200,122,writearray[0]); - //printf("\nRegister 7a=%d", writearray[0]); - //**Modify commands above with value required if output clock is required, - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: filtercal -* -* Detailed Description: -* Instructs RC filter calibration. (Register 0x7b). -* -\****************************************************************************/ -/* -int filtercal(void *pTuner) -{ - //writearray[0] = 1; - //I2CWriteByte (pTuner, 200,123,writearray[0]); - //printf("\nRegister 7b=%d", writearray[0]); - //return; - return E4000_1_SUCCESS; -} -*/ -/****************************************************************************\ -* Function: Qpeak() -* -* Detailed Description: -* The function configures the E4000 gains. -* Also sigma delta controller. (Register 0x82). -* -\****************************************************************************/ - -int Qpeak(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - writearray[0] = 1; - writearray[1] = 254; - status=I2CWriteArray(pTuner, 200,126,2,writearray); - //printf("\nRegister 7e=%d", writearray[0]); - //printf("\nRegister 7f=%d", writearray[1]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CWriteByte (pTuner, 200,130,0); - //printf("\nRegister 82=0"); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CWriteByte (pTuner, 200,36,5); - //printf("\nRegister 24=5"); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 32; - writearray[1] = 1; - status=I2CWriteArray(pTuner, 200,135,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - //printf("\nRegister 87=%d", writearray[0]); - //printf("\nRegister 88=%d", writearray[1]); - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: E4000_gain_freq() -* -* Detailed Description: -* The function configures the E4000 gains vs. freq -* 0xa3 to 0xa7. Also 0x24. -* -\****************************************************************************/ -int E4000_gain_freq(void *pTuner, int Freq) -{ - unsigned char writearray[5]; - int status; - - if (Freq<=350000) - { - writearray[0] = 0x10; - writearray[1] = 0x42; - writearray[2] = 0x09; - writearray[3] = 0x21; - writearray[4] = 0x94; - } - else if(Freq>=1000000) - { - writearray[0] = 0x10; - writearray[1] = 0x42; - writearray[2] = 0x09; - writearray[3] = 0x21; - writearray[4] = 0x94; - } - else - { - writearray[0] = 0x10; - writearray[1] = 0x42; - writearray[2] = 0x09; - writearray[3] = 0x21; - writearray[4] = 0x94; - } - status=I2CWriteArray(pTuner, 200,163,5,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if (Freq<=350000) - { - writearray[0] = 94; - writearray[1] = 6; - status=I2CWriteArray(pTuner, 200,159,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteArray(pTuner, 200,136,1,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - else - { - writearray[0] = 127; - writearray[1] = 7; - status=I2CWriteArray(pTuner, 200,159,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 1; - status=I2CWriteArray(pTuner, 200,136,1,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - - //printf("\nRegister 9f=%d", writearray[0]); - //printf("\nRegister a0=%d", writearray[1]); - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: DCoffloop -* -* Detailed Description: -* Populates DC offset LUT. (Registers 0x2d, 0x70, 0x71). -* Turns on DC offset LUT and time varying DC offset. -\****************************************************************************/ -int DCoffloop(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - //writearray[0]=0; - //I2CWriteByte(pTuner, 200,115,writearray[0]); - //printf("\nRegister 73=%d", writearray[0]); - writearray[0] = 31; - status=I2CWriteByte(pTuner, 200,45,writearray[0]); - //printf("\nRegister 2d=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 1; - writearray[1] = 1; - status=I2CWriteArray(pTuner, 200,112,2,writearray); - //printf("\nRegister 70=%d", writearray[0]); - //printf("\nRegister 71=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: commonmode -* -* Detailed Description: -* Configures common mode voltage. (Registers 0x2f). -* -\****************************************************************************/ -/* -int commonmode(void *pTuner) -{ - //writearray[0] = 0; - //I2CWriteByte(Device_address,47,writearray[0]); - //printf("\nRegister 0x2fh = %d", writearray[0]); - // Sets 550mV. Modify if alternative is desired. - return E4000_1_SUCCESS; -} -*/ -/****************************************************************************\ -* Function: GainControlinit -* -* Detailed Description: -* Configures gain control mode. (Registers 0x1d, 0x1e, 0x1f, 0x20, 0x21, -* 0x1a, 0x74h, 0x75h). -* User may wish to modify values depending on usage scenario. -* Routine configures LNA: autonomous gain control -* IF PWM gain control. -* PWM thresholds = default -* Mixer: switches when LNA gain =7.5dB -* Sensitivity / Linearity mode: manual switch -* -\****************************************************************************/ -int GainControlinit(void *pTuner) -{ - unsigned char writearray[5]; - unsigned char read1[1]; - int status; - - unsigned char sum=255; - - writearray[0] = 23; - status=I2CWriteByte(pTuner, 200,26,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - //printf("\nRegister 1a=%d", writearray[0]); - - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 16; - writearray[1] = 4; - writearray[2] = 26; - writearray[3] = 15; - writearray[4] = 167; - status=I2CWriteArray(pTuner, 200,29,5,writearray); - //printf("\nRegister 1d=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 81; - status=I2CWriteByte(pTuner, 200,134,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - //printf("\nRegister 86=%d", writearray[0]); - - //For Realtek - gain control logic - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if(read1[0]<=sum) - { - sum=read1[0]; - } - - status=I2CWriteByte(pTuner, 200,31,writearray[2]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if(read1[0] <= sum) - { - sum=read1[0]; - } - - status=I2CWriteByte(pTuner, 200,31,writearray[2]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if(read1[0] <= sum) - { - sum=read1[0]; - } - - status=I2CWriteByte(pTuner, 200,31,writearray[2]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if(read1[0] <= sum) - { - sum=read1[0]; - } - - status=I2CWriteByte(pTuner, 200,31,writearray[2]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CReadByte(pTuner, 201,27,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if (read1[0]<=sum) - { - sum=read1[0]; - } - - writearray[0]=sum; - status=I2CWriteByte(pTuner, 200,27,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - //printf("\nRegister 1b=%d", writearray[0]); - //printf("\nRegister 1e=%d", writearray[1]); - //printf("\nRegister 1f=%d", writearray[2]); - //printf("\nRegister 20=%d", writearray[3]); - //printf("\nRegister 21=%d", writearray[4]); - //writearray[0] = 3; - //writearray[1] = 252; - //writearray[2] = 3; - //writearray[3] = 252; - //I2CWriteArray(pTuner, 200,116,4,writearray); - //printf("\nRegister 74=%d", writearray[0]); - //printf("\nRegister 75=%d", writearray[1]); - //printf("\nRegister 76=%d", writearray[2]); - //printf("\nRegister 77=%d", writearray[3]); - - return E4000_1_SUCCESS; -} - -/****************************************************************************\ -* Main program -* -* -* -\****************************************************************************/ -/* -int main() -{ - tunerreset (); - Tunerclock(); - //filtercal(); - Qpeak(); - //PLL(Ref_clk, Freq); - //LNAfilter(Freq); - //IFfilter(bandwidth, Ref_clk); - //freqband(Freq); - //DCoffLUT(); - DCoffloop(); - //commonmode(); - GainControlinit(); -// system("PAUSE"); - return(0); -} -*/ - - -// Elonics source code - frequency_change_rev2.04_realtek.c - - -//****************************************************************************/ -// -// Filename E4000_freqchangerev2.04.c -// Revision 2.04 -// -// Description: -// Frequency change script for the Elonics E4000 revB tuner -// -// Copyright (c) Elonics Ltd -// -// Any software supplied free of charge for use with elonics -// evaluation kits is supplied without warranty and for -// evaluation purposes only. Incorporation of any of this -// code into products for open sale is permitted but only at -// the user's own risk. Elonics accepts no liability for the -// integrity of this software whatsoever. -// -// -//****************************************************************************/ -//#include -//#include -// -// User defined variable definitions -// -/* -int Ref_clk = 20000; // Reference clock frequency(kHz). -int Freq = 590000; // RF Frequency (kHz) -int bandwidth = 8; //RF channel bandwith (MHz) -*/ -// -// API defined variable definitions -//int VCO_freq; -//unsigned char writearray[5]; -//unsigned char read1[1]; -//int E4000_1_SUCCESS; -//int E4000_1_FAIL; -//int E4000_I2C_SUCCESS; -//int status; -// -// -// function definitions -// -/* -int Gainmanual(); -int PLL(int Ref_clk, int Freq); -int LNAfilter(int Freq); -int IFfilter(int bandwidth, int Ref_clk); -int freqband(int Freq); -int DCoffLUT(); -int GainControlauto(); -*/ -// -//**************************************************************************** -// --- Public functions ------------------------------------------------------ -/****************************************************************************\ -//****************************************************************************\ -* Function: Gainmanual -* -* Detailed Description: -* Sets Gain control to serial interface control. -* -\****************************************************************************/ -int Gainmanual(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - writearray[0]=0; - status=I2CWriteByte(pTuner, 200,26,writearray[0]); - //printf("\nRegister 1a=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteByte (pTuner, 200,9,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteByte (pTuner, 200,5,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} - -/****************************************************************************\ -* Function: PLL -* -* Detailed Description: -* Configures E4000 PLL divider & sigma delta. 0x0d,0x09, 0x0a, 0x0b). -* -\****************************************************************************/ -int PLL(void *pTuner, int Ref_clk, int Freq) -{ - int VCO_freq; - unsigned char writearray[5]; - int status; - - unsigned char divider; - int intVCOfreq; - int SigDel; - int SigDel2; - int SigDel3; -// int harmonic_freq; -// int offset; - - if (Freq<=72400) - { - writearray[4] = 15; - VCO_freq=Freq*48; - } - else if (Freq<=81200) - { - writearray[4] = 14; - VCO_freq=Freq*40; - } - else if (Freq<=108300) - { - writearray[4]=13; - VCO_freq=Freq*32; - } - else if (Freq<=162500) - { - writearray[4]=12; - VCO_freq=Freq*24; - } - else if (Freq<=216600) - { - writearray[4]=11; - VCO_freq=Freq*16; - } - else if (Freq<=325000) - { - writearray[4]=10; - VCO_freq=Freq*12; - } - else if (Freq<=350000) - { - writearray[4]=9; - VCO_freq=Freq*8; - } - else if (Freq<=432000) - { - writearray[4]=3; - VCO_freq=Freq*8; - } - else if (Freq<=667000) - { - writearray[4]=2; - VCO_freq=Freq*6; - } - else if (Freq<=1200000) - { - writearray[4]=1; - VCO_freq=Freq*4; - } - else - { - writearray[4]=0; - VCO_freq=Freq*2; - } - - //printf("\nVCOfreq=%d", VCO_freq); -// divider = VCO_freq * 1000 / Ref_clk; - divider = VCO_freq / Ref_clk; - //printf("\ndivider=%d", divider); - writearray[0]= divider; -// intVCOfreq = divider * Ref_clk /1000; - intVCOfreq = divider * Ref_clk; - //printf("\ninteger VCO freq=%d", intVCOfreq); -// SigDel=65536 * 1000 * (VCO_freq - intVCOfreq) / Ref_clk; - SigDel=65536 * (VCO_freq - intVCOfreq) / Ref_clk; - //printf("\nSigma delta=%d", SigDel); - if (SigDel<=1024) - { - SigDel = 1024; - } - else if (SigDel>=64512) - { - SigDel=64512; - } - SigDel2 = SigDel / 256; - //printf("\nSigdel2=%d", SigDel2); - writearray[2] = (unsigned char)SigDel2; - SigDel3 = SigDel - (256 * SigDel2); - //printf("\nSig del3=%d", SigDel3); - writearray[1]= (unsigned char)SigDel3; - writearray[3]=(unsigned char)0; - status=I2CWriteArray(pTuner, 200,9,5,writearray); - //printf("\nRegister 9=%d", writearray[0]); - //printf("\nRegister a=%d", writearray[1]); - //printf("\nRegister b=%d", writearray[2]); - //printf("\nRegister d=%d", writearray[4]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - if (Freq<=82900) - { - writearray[0]=0; - writearray[2]=1; - } - else if (Freq<=89900) - { - writearray[0]=3; - writearray[2]=9; - } - else if (Freq<=111700) - { - writearray[0]=0; - writearray[2]=1; - } - else if (Freq<=118700) - { - writearray[0]=3; - writearray[2]=1; - } - else if (Freq<=140500) - { - writearray[0]=0; - writearray[2]=3; - } - else if (Freq<=147500) - { - writearray[0]=3; - writearray[2]=11; - } - else if (Freq<=169300) - { - writearray[0]=0; - writearray[2]=3; - } - else if (Freq<=176300) - { - writearray[0]=3; - writearray[2]=11; - } - else if (Freq<=198100) - { - writearray[0]=0; - writearray[2]=3; - } - else if (Freq<=205100) - { - writearray[0]=3; - writearray[2]=19; - } - else if (Freq<=226900) - { - writearray[0]=0; - writearray[2]=3; - } - else if (Freq<=233900) - { - writearray[0]=3; - writearray[2]=3; - } - else if (Freq<=350000) - { - writearray[0]=0; - writearray[2]=3; - } - else if (Freq<=485600) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=493600) - { - writearray[0]=3; - writearray[2]=5; - } - else if (Freq<=514400) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=522400) - { - writearray[0]=3; - writearray[2]=5; - } - else if (Freq<=543200) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=551200) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=572000) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=580000) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=600800) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=608800) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=629600) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=637600) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=658400) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=666400) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=687200) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=695200) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=716000) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=724000) - { - writearray[0]=3; - writearray[2]=13; - } - else if (Freq<=744800) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=752800) - { - writearray[0]=3; - writearray[2]=21; - } - else if (Freq<=773600) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=781600) - { - writearray[0]=3; - writearray[2]=21; - } - else if (Freq<=802400) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=810400) - { - writearray[0]=3; - writearray[2]=21; - } - else if (Freq<=831200) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=839200) - { - writearray[0]=3; - writearray[2]=21; - } - else if (Freq<=860000) - { - writearray[0]=0; - writearray[2]=5; - } - else if (Freq<=868000) - { - writearray[0]=3; - writearray[2]=21; - } - else - { - writearray[0]=0; - writearray[2]=7; - } - - status=I2CWriteByte (pTuner, 200,7,writearray[2]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - status=I2CWriteByte (pTuner, 200,5,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} - -/****************************************************************************\ -* Function: LNAfilter -* -* Detailed Description: -* The function configures the E4000 LNA filter. (Register 0x10). -* -\****************************************************************************/ - -int LNAfilter(void *pTuner, int Freq) -{ - unsigned char writearray[5]; - int status; - - if(Freq<=370000) - { - writearray[0]=0; - } - else if(Freq<=392500) - { - writearray[0]=1; - } - else if(Freq<=415000) - { - writearray[0] =2; - } - else if(Freq<=437500) - { - writearray[0]=3; - } - else if(Freq<=462500) - { - writearray[0]=4; - } - else if(Freq<=490000) - { - writearray[0]=5; - } - else if(Freq<=522500) - { - writearray[0]=6; - } - else if(Freq<=557500) - { - writearray[0]=7; - } - else if(Freq<=595000) - { - writearray[0]=8; - } - else if(Freq<=642500) - { - writearray[0]=9; - } - else if(Freq<=695000) - { - writearray[0]=10; - } - else if(Freq<=740000) - { - writearray[0]=11; - } - else if(Freq<=800000) - { - writearray[0]=12; - } - else if(Freq<=865000) - { - writearray[0] =13; - } - else if(Freq<=930000) - { - writearray[0]=14; - } - else if(Freq<=1000000) - { - writearray[0]=15; - } - else if(Freq<=1310000) - { - writearray[0]=0; - } - else if(Freq<=1340000) - { - writearray[0]=1; - } - else if(Freq<=1385000) - { - writearray[0]=2; - } - else if(Freq<=1427500) - { - writearray[0]=3; - } - else if(Freq<=1452500) - { - writearray[0]=4; - } - else if(Freq<=1475000) - { - writearray[0]=5; - } - else if(Freq<=1510000) - { - writearray[0]=6; - } - else if(Freq<=1545000) - { - writearray[0]=7; - } - else if(Freq<=1575000) - { - writearray[0] =8; - } - else if(Freq<=1615000) - { - writearray[0]=9; - } - else if(Freq<=1650000) - { - writearray[0] =10; - } - else if(Freq<=1670000) - { - writearray[0]=11; - } - else if(Freq<=1690000) - { - writearray[0]=12; - } - else if(Freq<=1710000) - { - writearray[0]=13; - } - else if(Freq<=1735000) - { - writearray[0]=14; - } - else - { - writearray[0]=15; - } - status=I2CWriteByte (pTuner, 200,16,writearray[0]); - //printf("\nRegister 10=%d", writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: IFfilter -* -* Detailed Description: -* The function configures the E4000 IF filter. (Register 0x11,0x12). -* -\****************************************************************************/ -int IFfilter(void *pTuner, int bandwidth, int Ref_clk) -{ - unsigned char writearray[5]; - int status; - - int IF_BW; - - IF_BW = bandwidth / 2; - if(IF_BW<=2150) - { - writearray[0]=253; - writearray[1]=31; - } - else if(IF_BW<=2200) - { - writearray[0]=253; - writearray[1]=30; - } - else if(IF_BW<=2240) - { - writearray[0]=252; - writearray[1]=29; - } - else if(IF_BW<=2280) - { - writearray[0]=252; - writearray[1]=28; - } - else if(IF_BW<=2300) - { - writearray[0]=252; - writearray[1]=27; - } - else if(IF_BW<=2400) - { - writearray[0]=252; - writearray[1]=26; - } - else if(IF_BW<=2450) - { - writearray[0]=252; - writearray[1]=25; - } - else if(IF_BW<=2500) - { - writearray[0]=252; - writearray[1]=24; - } - else if(IF_BW<=2550) - { - writearray[0]=252; - writearray[1]=23; - } - else if(IF_BW<=2600) - { - writearray[0]=252; - writearray[1]=22; - } - else if(IF_BW<=2700) - { - writearray[0]=252; - writearray[1]=21; - } - else if(IF_BW<=2750) - { - writearray[0]=252; - writearray[1]=20; - } - else if(IF_BW<=2800) - { - writearray[0]=252; - writearray[1]=19; - } - else if(IF_BW<=2900) - { - writearray[0]=251; - writearray[1]=18; - } - else if(IF_BW<=2950) - { - writearray[0]=251; - writearray[1]=17; - } - else if(IF_BW<=3000) - { - writearray[0]=251; - writearray[1]=16; - } - else if(IF_BW<=3100) - { - writearray[0]=251; - writearray[1]=15; - } - else if(IF_BW<=3200) - { - writearray[0]=250; - writearray[1]=14; - } - else if(IF_BW<=3300) - { - writearray[0]=250; - writearray[1]=13; - } - else if(IF_BW<=3400) - { - writearray[0]=249; - writearray[1]=12; - } - else if(IF_BW<=3600) - { - writearray[0]=249; - writearray[1]=11; - } - else if(IF_BW<=3700) - { - writearray[0]=249; - writearray[1]=10; - } - else if(IF_BW<=3800) - { - writearray[0]=248; - writearray[1]=9; - } - else if(IF_BW<=3900) - { - writearray[0]=248; - writearray[1]=8; - } - else if(IF_BW<=4100) - { - writearray[0]=248; - writearray[1]=7; - } - else if(IF_BW<=4300) - { - writearray[0]=247; - writearray[1]=6; - } - else if(IF_BW<=4400) - { - writearray[0]=247; - writearray[1]=5; - } - else if(IF_BW<=4600) - { - writearray[0]=247; - writearray[1]=4; - } - else if(IF_BW<=4800) - { - writearray[0]=246; - writearray[1]=3; - } - else if(IF_BW<=5000) - { - writearray[0]=246; - writearray[1]=2; - } - else if(IF_BW<=5300) - { - writearray[0]=245; - writearray[1]=1; - } - else if(IF_BW<=5500) - { - writearray[0]=245; - writearray[1]=0; - } - else - { - writearray[0]=0; - writearray[1]=32; - } - status=I2CWriteArray(pTuner, 200,17,2,writearray); - //printf("\nRegister 11=%d", writearray[0]); - //printf("\nRegister 12=%d", writearray[1]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: freqband -* -* Detailed Description: -* Configures the E4000 frequency band. (Registers 0x07, 0x78). -* -\****************************************************************************/ -int freqband(void *pTuner, int Freq) -{ - unsigned char writearray[5]; - int status; - - if (Freq<=140000) - { - writearray[0] = 3; - status=I2CWriteByte(pTuner, 200,120,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - else if (Freq<=350000) - { - writearray[0] = 3; - status=I2CWriteByte(pTuner, 200,120,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - else if (Freq<=1000000) - { - writearray[0] = 3; - status=I2CWriteByte(pTuner, 200,120,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - else - { - writearray[0] = 7; - status=I2CWriteByte(pTuner, 200,7,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = 0; - status=I2CWriteByte(pTuner, 200,120,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: DCoffLUT -* -* Detailed Description: -* Populates DC offset LUT. (Registers 0x50 - 0x53, 0x60 - 0x63). -* -\****************************************************************************/ -int DCoffLUT(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - unsigned char read1[1]; - unsigned char IOFF; - unsigned char QOFF; - unsigned char RANGE1; -// unsigned char RANGE2; - unsigned char QRANGE; - unsigned char IRANGE; - writearray[0] = 0; - writearray[1] = 126; - writearray[2] = 36; - status=I2CWriteArray(pTuner, 200,21,3,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Sets mixer & IF stage 1 gain = 00 and IF stg 2+ to max gain. - writearray[0] = 1; - status=I2CWriteByte(pTuner, 200,41,writearray[0]); - // Instructs a DC offset calibration. - status=I2CReadByte(pTuner, 201,42,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IOFF=read1[0]; - status=I2CReadByte(pTuner, 201,43,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - QOFF=read1[0]; - status=I2CReadByte(pTuner, 201,44,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - RANGE1=read1[0]; - //reads DC offset values back - if(RANGE1>=32) - { - RANGE1 = RANGE1 -32; - } - if(RANGE1>=16) - { - RANGE1 = RANGE1 - 16; - } - IRANGE=RANGE1; - QRANGE = (read1[0] - RANGE1) / 16; - - writearray[0] = (IRANGE * 64) + IOFF; - status=I2CWriteByte(pTuner, 200,96,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = (QRANGE * 64) + QOFF; - status=I2CWriteByte(pTuner, 200,80,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Populate DC offset LUT - writearray[0] = 0; - writearray[1] = 127; - status=I2CWriteArray(pTuner, 200,21,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Sets mixer & IF stage 1 gain = 01 leaving IF stg 2+ at max gain. - writearray[0]= 1; - status=I2CWriteByte(pTuner, 200,41,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Instructs a DC offset calibration. - status=I2CReadByte(pTuner, 201,42,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IOFF=read1[0]; - status=I2CReadByte(pTuner, 201,43,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - QOFF=read1[0]; - status=I2CReadByte(pTuner, 201,44,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - RANGE1=read1[0]; - // Read DC offset values - if(RANGE1>=32) - { - RANGE1 = RANGE1 -32; - } - if(RANGE1>=16) - { - RANGE1 = RANGE1 - 16; - } - IRANGE = RANGE1; - QRANGE = (read1[0] - RANGE1) / 16; - - writearray[0] = (IRANGE * 64) + IOFF; - status=I2CWriteByte(pTuner, 200,97,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = (QRANGE * 64) + QOFF; - status=I2CWriteByte(pTuner, 200,81,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Populate DC offset LUT - writearray[0] = 1; - status=I2CWriteByte(pTuner, 200,21,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Sets mixer & IF stage 1 gain = 11 leaving IF stg 2+ at max gain. - writearray[0] = 1; - status=I2CWriteByte(pTuner, 200,41,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Instructs a DC offset calibration. - status=I2CReadByte(pTuner, 201,42,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IOFF=read1[0]; - status=I2CReadByte(pTuner, 201,43,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - QOFF=read1[0]; - status=I2CReadByte(pTuner, 201,44,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - RANGE1 = read1[0]; - // Read DC offset values - if(RANGE1>=32) - { - RANGE1 = RANGE1 -32; - } - if(RANGE1>=16) - { - RANGE1 = RANGE1 - 16; - } - IRANGE = RANGE1; - QRANGE = (read1[0] - RANGE1) / 16; - writearray[0] = (IRANGE * 64) + IOFF; - status=I2CWriteByte(pTuner, 200,99,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = (QRANGE * 64) + QOFF; - status=I2CWriteByte(pTuner, 200,83,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Populate DC offset LUT - writearray[0] = 126; - status=I2CWriteByte(pTuner, 200,22,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Sets mixer & IF stage 1 gain = 11 leaving IF stg 2+ at max gain. - writearray[0] = 1; - status=I2CWriteByte(pTuner, 200,41,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - // Instructs a DC offset calibration. - status=I2CReadByte(pTuner, 201,42,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - IOFF=read1[0]; - - status=I2CReadByte(pTuner, 201,43,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - QOFF=read1[0]; - - status=I2CReadByte(pTuner, 201,44,read1); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - RANGE1=read1[0]; - - // Read DC offset values - if(RANGE1>=32) - { - RANGE1 = RANGE1 -32; - } - if(RANGE1>=16) - { - RANGE1 = RANGE1 - 16; - } - IRANGE = RANGE1; - QRANGE = (read1[0] - RANGE1) / 16; - - writearray[0]=(IRANGE * 64) + IOFF; - status=I2CWriteByte(pTuner, 200,98,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - writearray[0] = (QRANGE * 64) + QOFF; - status=I2CWriteByte(pTuner, 200,82,writearray[0]); - // Populate DC offset LUT - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: GainControlinit -* -* Detailed Description: -* Configures gain control mode. (Registers 0x1a) -* -\****************************************************************************/ -int GainControlauto(void *pTuner) -{ - unsigned char writearray[5]; - int status; - - writearray[0] = 23; - status=I2CWriteByte(pTuner, 200,26,writearray[0]); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Main program -* -* -* -\****************************************************************************/ -/* -int main() -{ - Gainmanual(); - PLL(Ref_clk, Freq); - LNAfilter(Freq); - IFfilter(bandwidth, Ref_clk); - freqband(Freq); - DCoffLUT(); - GainControlauto(); - return(0); -} -*/ - -// Elonics source code - RT2832_SW_optimisation_rev2.c - - - -/****************************************************************************\ -* Function: E4000_sensitivity -* -* Detailed Description: -* The function configures the E4000 for sensitivity mode. -* -\****************************************************************************/ - -int E4000_sensitivity(void *pTuner, int Freq, int bandwidth) -{ - unsigned char writearray[2]; - int status; - int IF_BW; - - writearray[1]=0x00; - - if(Freq<=700000) - { - writearray[0] = 0x07; - } - else - { - writearray[0] = 0x05; - } - status = I2CWriteArray(pTuner,200,36,1,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IF_BW = bandwidth / 2; - if(IF_BW<=2500) - { - writearray[0]=0xfc; - writearray[1]=0x17; - } - else if(IF_BW<=3000) - { - writearray[0]=0xfb; - writearray[1]=0x0f; - } - else if(IF_BW<=3500) - { - writearray[0]=0xf9; - writearray[1]=0x0b; - } - else if(IF_BW<=4000) - { - writearray[0]=0xf8; - writearray[1]=0x07; - } - status = I2CWriteArray(pTuner,200,17,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: E4000_linearity -* -* Detailed Description: -* The function configures the E4000 for linearity mode. -* -\****************************************************************************/ -int E4000_linearity(void *pTuner, int Freq, int bandwidth) -{ - - unsigned char writearray[2]; - int status; - int IF_BW; - - writearray[1]=0x00; - - if(Freq<=700000) - { - writearray[0] = 0x03; - } - else - { - writearray[0] = 0x01; - } - status = I2CWriteArray(pTuner,200,36,1,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IF_BW = bandwidth / 2; - if(IF_BW<=2500) - { - writearray[0]=0xfe; - writearray[1]=0x19; - } - else if(IF_BW<=3000) - { - writearray[0]=0xfd; - writearray[1]=0x11; - } - else if(IF_BW<=3500) - { - writearray[0]=0xfb; - writearray[1]=0x0d; - } - else if(IF_BW<=4000) - { - writearray[0]=0xfa; - writearray[1]=0x0a; - } - status = I2CWriteArray(pTuner,200,17,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} -/****************************************************************************\ -* Function: E4000_nominal -* -* Detailed Description: -* The function configures the E4000 for nominal -* -\****************************************************************************/ -int E4000_nominal(void *pTuner, int Freq, int bandwidth) -{ - unsigned char writearray[2]; - int status; - int IF_BW; - - writearray[1]=0x00; - - if(Freq<=700000) - { - writearray[0] = 0x03; - } - else - { - writearray[0] = 0x01; - } - status = I2CWriteArray(pTuner,200,36,1,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - IF_BW = bandwidth / 2; - if(IF_BW<=2500) - { - writearray[0]=0xfc; - writearray[1]=0x17; - } - else if(IF_BW<=3000) - { - writearray[0]=0xfb; - writearray[1]=0x0f; - } - else if(IF_BW<=3500) - { - writearray[0]=0xf9; - writearray[1]=0x0b; - } - else if(IF_BW<=4000) - { - writearray[0]=0xf8; - writearray[1]=0x07; - } - status = I2CWriteArray(pTuner,200,17,2,writearray); - if(status != E4000_I2C_SUCCESS) - { - return E4000_1_FAIL; - } - - return E4000_1_SUCCESS; -} - diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c new file mode 100644 index 0000000..b530812 --- /dev/null +++ b/src/tuner_e4k.c @@ -0,0 +1,955 @@ +/* (C) 2011-2012 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* If this is defined, the limits are somewhat relaxed compared to what the + * vendor claims is possible */ +#define OUT_OF_SPEC + +#define MHZ(x) ((x)*1000*1000) +#define KHZ(x) ((x)*1000) + +uint32_t unsigned_delta(uint32_t a, uint32_t b) +{ + if (a > b) + return a - b; + else + return b - a; +} + +/* look-up table bit-width -> mask */ +static const uint8_t width2mask[] = { + 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff +}; + +/*********************************************************************** + * Register Access */ + +#if 0 +/*! \brief Write a register of the tuner chip + * \param[in] e4k reference to the tuner + * \param[in] reg number of the register + * \param[in] val value to be written + * \returns 0 on success, negative in case of error + */ +int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) +{ + /* FIXME */ + return 0; +} + +/*! \brief Read a register of the tuner chip + * \param[in] e4k reference to the tuner + * \param[in] reg number of the register + * \returns positive 8bit register contents on success, negative in case of error + */ +int e4k_reg_read(struct e4k_state *e4k, uint8_t reg) +{ + /* FIXME */ + return 0; +} +#endif + +/*! \brief Set or clear some (masked) bits inside a register + * \param[in] e4k reference to the tuner + * \param[in] reg number of the register + * \param[in] mask bit-mask of the value + * \param[in] val data value to be written to register + * \returns 0 on success, negative in case of error + */ +static int e4k_reg_set_mask(struct e4k_state *e4k, uint8_t reg, + uint8_t mask, uint8_t val) +{ + uint8_t tmp = e4k_reg_read(e4k, reg); + + if ((tmp & mask) == val) + return 0; + + return e4k_reg_write(e4k, reg, (tmp & ~mask) | (val & mask)); +} + +/*! \brief Write a given field inside a register + * \param[in] e4k reference to the tuner + * \param[in] field structure describing the field + * \param[in] val value to be written + * \returns 0 on success, negative in case of error + */ +static int e4k_field_write(struct e4k_state *e4k, const struct reg_field *field, uint8_t val) +{ + int rc; + uint8_t mask; + + rc = e4k_reg_read(e4k, field->reg); + if (rc < 0) + return rc; + + mask = width2mask[field->width] << field->shift; + + return e4k_reg_set_mask(e4k, field->reg, mask, val << field->shift); +} + +/*! \brief Read a given field inside a register + * \param[in] e4k reference to the tuner + * \param[in] field structure describing the field + * \returns positive value of the field, negative in case of error + */ +static int e4k_field_read(struct e4k_state *e4k, const struct reg_field *field) +{ + int rc; + + rc = e4k_reg_read(e4k, field->reg); + if (rc < 0) + return rc; + + rc = (rc >> field->shift) & width2mask[field->width]; + + return rc; +} + +/*********************************************************************** + * Filter Control */ + +static const uint32_t rf_filt_center_uhf[] = { + MHZ(360), MHZ(380), MHZ(405), MHZ(425), + MHZ(450), MHZ(475), MHZ(505), MHZ(540), + MHZ(575), MHZ(615), MHZ(670), MHZ(720), + MHZ(760), MHZ(840), MHZ(890), MHZ(970) +}; + +static const uint32_t rf_filt_center_l[] = { + MHZ(1300), MHZ(1320), MHZ(1360), MHZ(1410), + MHZ(1445), MHZ(1460), MHZ(1490), MHZ(1530), + MHZ(1560), MHZ(1590), MHZ(1640), MHZ(1660), + MHZ(1680), MHZ(1700), MHZ(1720), MHZ(1750) +}; + +static int closest_arr_idx(const uint32_t *arr, unsigned int arr_size, uint32_t freq) +{ + unsigned int i, bi = 0; + uint32_t best_delta = 0xffffffff; + + /* iterate over the array containing a list of the center + * frequencies, selecting the closest one */ + for (i = 0; i < arr_size; i++) { + uint32_t delta = unsigned_delta(freq, arr[i]); + if (delta < best_delta) { + best_delta = delta; + bi = i; + } + } + + return bi; +} + +/* return 4-bit index as to which RF filter to select */ +static int choose_rf_filter(uint32_t freq) +{ + int rc; + + if (freq < MHZ(350)) + rc = 0; + else if (freq < MHZ(1000)) + rc = closest_arr_idx(rf_filt_center_uhf, + ARRAY_SIZE(rf_filt_center_uhf), + freq); + else + rc = closest_arr_idx(rf_filt_center_l, + ARRAY_SIZE(rf_filt_center_l), + freq); + + return rc; +} + +/* \brief Automatically select apropriate RF filter based on e4k state */ +int e4k_rf_filter_set(struct e4k_state *e4k) +{ + int rc; + + rc = choose_rf_filter(e4k->vco.flo); + if (rc < 0) + return rc; + + return e4k_reg_set_mask(e4k, E4K_REG_FILT1, 0xF, rc); +} + +/* Mixer Filter */ +static const uint32_t mix_filter_bw[] = { + KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), + KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), + KHZ(4600), KHZ(4200), KHZ(3800), KHZ(3400), + KHZ(3300), KHZ(2700), KHZ(2300), KHZ(1900) +}; + +/* IF RC Filter */ +static const uint32_t ifrc_filter_bw[] = { + KHZ(21400), KHZ(21000), KHZ(17600), KHZ(14700), + KHZ(12400), KHZ(10600), KHZ(9000), KHZ(7700), + KHZ(6400), KHZ(5300), KHZ(4400), KHZ(3400), + KHZ(2600), KHZ(1800), KHZ(1200), KHZ(1000) +}; + +/* IF Channel Filter */ +static const uint32_t ifch_filter_bw[] = { + KHZ(5500), KHZ(5300), KHZ(5000), KHZ(4800), + KHZ(4600), KHZ(4400), KHZ(4300), KHZ(4100), + KHZ(3900), KHZ(3800), KHZ(3700), KHZ(3600), + KHZ(3400), KHZ(3300), KHZ(3200), KHZ(3100), + KHZ(3000), KHZ(2950), KHZ(2900), KHZ(2800), + KHZ(2750), KHZ(2700), KHZ(2600), KHZ(2550), + KHZ(2500), KHZ(2450), KHZ(2400), KHZ(2300), + KHZ(2280), KHZ(2240), KHZ(2200), KHZ(2150) +}; + +static const uint32_t *if_filter_bw[] = { + mix_filter_bw, + ifch_filter_bw, + ifrc_filter_bw, +}; + +static const uint32_t if_filter_bw_len[] = { + ARRAY_SIZE(mix_filter_bw), + ARRAY_SIZE(ifch_filter_bw), + ARRAY_SIZE(ifrc_filter_bw), +}; + +static const struct reg_field if_filter_fields[] = { + { + E4K_REG_FILT2, 4, 4, + }, + { + E4K_REG_FILT3, 0, 5, + }, + { + E4K_REG_FILT2, 0, 4, + } +}; + +static int find_if_bw(enum e4k_if_filter filter, uint32_t bw) +{ + if (filter >= ARRAY_SIZE(if_filter_bw)) + return -EINVAL; + + return closest_arr_idx(if_filter_bw[filter], + if_filter_bw_len[filter], bw); +} + +/*! \brief Set the filter band-width of any of the IF filters + * \param[in] e4k reference to the tuner chip + * \param[in] filter filter to be configured + * \param[in] bandwidth bandwidth to be configured + * \returns positive actual filter band-width, negative in case of error + */ +int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, + uint32_t bandwidth) +{ + int bw_idx; + const struct reg_field *field; + + if (filter >= ARRAY_SIZE(if_filter_bw)) + return -EINVAL; + + bw_idx = find_if_bw(filter, bandwidth); + + field = &if_filter_fields[filter]; + + return e4k_field_write(e4k, field, bw_idx); +} + +/*! \brief Enables / Disables the channel filter + * \param[in] e4k reference to the tuner chip + * \param[in] on 1=filter enabled, 0=filter disabled + * \returns 0 success, negative errors + */ +int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on) +{ + return e4k_reg_set_mask(e4k, E4K_REG_FILT3, E4K_FILT3_DISABLE, + on ? 0 : E4K_FILT3_DISABLE); +} + +int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) +{ + const uint32_t *arr; + int rc; + const struct reg_field *field; + + if (filter >= ARRAY_SIZE(if_filter_bw)) + return -EINVAL; + + field = &if_filter_fields[filter]; + + rc = e4k_field_read(e4k, field); + if (rc < 0) + return rc; + + arr = if_filter_bw[filter]; + + return arr[rc]; +} + + +/*********************************************************************** + * Frequency Control */ + +#define E4K_FVCO_MIN_KHZ 2600000 /* 2.6 GHz */ +#define E4K_FVCO_MAX_KHZ 3900000 /* 3.9 GHz */ +#define E4K_PLL_Y 65536 + +#ifdef OUT_OF_SPEC +#define E4K_FLO_MIN_MHZ 50 +#define E4K_FLO_MAX_MHZ 2200UL +#else +#define E4K_FLO_MIN_MHZ 64 +#define E4K_FLO_MAX_MHZ 1700 +#endif + +struct pll_settings { + uint32_t freq; + uint8_t reg_synth7; + uint8_t mult; +}; + +static const struct pll_settings pll_vars[] = { + {KHZ(72400), (1 << 3) | 7, 48}, + {KHZ(81200), (1 << 3) | 6, 40}, + {KHZ(108300), (1 << 3) | 5, 32}, + {KHZ(162500), (1 << 3) | 4, 24}, + {KHZ(216600), (1 << 3) | 3, 16}, + {KHZ(325000), (1 << 3) | 2, 12}, + {KHZ(350000), (1 << 3) | 1, 8}, + {KHZ(432000), (0 << 3) | 3, 8}, + {KHZ(667000), (0 << 3) | 2, 6}, + {KHZ(1200000), (0 << 3) | 1, 4} +}; + +static int is_fvco_valid(uint32_t fvco_z) +{ + /* check if the resulting fosc is valid */ + if (fvco_z/1000 < E4K_FVCO_MIN_KHZ || + fvco_z/1000 > E4K_FVCO_MAX_KHZ) { + fprintf(stderr, "Fvco %u invalid\n", fvco_z); + return 0; + } + + return 1; +} + +static int is_fosc_valid(uint32_t fosc) +{ + if (fosc < MHZ(16) || fosc > MHZ(30)) { + fprintf(stderr, "Fosc %u invalid\n", fosc); + return 0; + } + + return 1; +} + +static int is_z_valid(uint32_t z) +{ + if (z > 255) { + fprintf(stderr, "Z %u invalid\n", z); + return 0; + } + + return 1; +} + +/*! \brief Determine if 3-phase mixing shall be used or not */ +static int use_3ph_mixing(uint32_t flo) +{ + /* this is a magic number somewhre between VHF and UHF */ + if (flo < MHZ(350)) + return 1; + + return 0; +} + +/* \brief compute Fvco based on Fosc, Z and X + * \returns positive value (Fvco in Hz), 0 in case of error */ +static uint64_t compute_fvco(uint32_t f_osc, uint8_t z, uint16_t x) +{ + uint64_t fvco_z, fvco_x, fvco; + + /* We use the following transformation in order to + * handle the fractional part with integer arithmetic: + * Fvco = Fosc * (Z + X/Y) <=> Fvco = Fosc * Z + (Fosc * X)/Y + * This avoids X/Y = 0. However, then we would overflow a 32bit + * integer, as we cannot hold e.g. 26 MHz * 65536 either. + */ + fvco_z = (uint64_t)f_osc * z; + +#if 0 + if (!is_fvco_valid(fvco_z)) + return 0; +#endif + + fvco_x = ((uint64_t)f_osc * x) / E4K_PLL_Y; + + fvco = fvco_z + fvco_x; + + return fvco; +} + +static int compute_flo(uint32_t f_osc, uint8_t z, uint16_t x, uint8_t r) +{ + uint64_t fvco = compute_fvco(f_osc, z, x); + if (fvco == 0) + return -EINVAL; + + return fvco / r; +} + +static int e4k_band_set(struct e4k_state *e4k, enum e4k_band band) +{ + int rc; + + switch (band) { + case E4K_BAND_VHF2: + case E4K_BAND_VHF3: + case E4K_BAND_UHF: + e4k_reg_write(e4k, E4K_REG_BIAS, 3); + break; + case E4K_BAND_L: + e4k_reg_write(e4k, E4K_REG_BIAS, 0); + break; + } + + rc = e4k_reg_set_mask(e4k, E4K_REG_SYNTH1, 0x06, band << 1); + if (rc >= 0) + e4k->band = band; + + return rc; +} + +/*! \brief Compute PLL parameters for givent target frequency + * \param[out] oscp Oscillator parameters, if computation successful + * \param[in] fosc Clock input frequency applied to the chip (Hz) + * \param[in] intended_flo target tuning frequency (Hz) + * \returns actual PLL frequency, as close as possible to intended_flo, + * negative in case of error + */ +int e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo) +{ + uint32_t i; + uint8_t r = 2; + uint64_t intended_fvco, remainder; + uint64_t z = 0; + uint32_t x; + int flo; + int three_phase_mixing = 0; + oscp->r_idx = 0; + + if (!is_fosc_valid(fosc)) + return -EINVAL; + + for(i = 0; i < ARRAY_SIZE(pll_vars); ++i) { + if(intended_flo < pll_vars[i].freq) { + three_phase_mixing = (pll_vars[i].reg_synth7 & 0x08) ? 1 : 0; + oscp->r_idx = pll_vars[i].reg_synth7; + r = pll_vars[i].mult; + break; + } + } + + //fprintf(stderr, "Fint=%u, R=%u\n", intended_flo, r); + + /* flo(max) = 1700MHz, R(max) = 48, we need 64bit! */ + intended_fvco = (uint64_t)intended_flo * r; + + /* compute integral component of multiplier */ + z = intended_fvco / fosc; + + /* compute fractional part. this will not overflow, + * as fosc(max) = 30MHz and z(max) = 255 */ + remainder = intended_fvco - (fosc * z); + /* remainder(max) = 30MHz, E4K_PLL_Y = 65536 -> 64bit! */ + x = (remainder * E4K_PLL_Y) / fosc; + /* x(max) as result of this computation is 65536 */ + + flo = compute_flo(fosc, z, x, r); + + oscp->fosc = fosc; + oscp->flo = flo; + oscp->intended_flo = intended_flo; + oscp->r = r; +// oscp->r_idx = pll_vars[i].reg_synth7 & 0x0; + oscp->threephase = three_phase_mixing; + oscp->x = x; + oscp->z = z; + + return flo; +} + +int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p) +{ + uint8_t val; + + /* program R + 3phase/2phase */ + e4k_reg_write(e4k, E4K_REG_SYNTH7, p->r_idx); + /* program Z */ + e4k_reg_write(e4k, E4K_REG_SYNTH3, p->z); + /* program X */ + e4k_reg_write(e4k, E4K_REG_SYNTH4, p->x & 0xff); + e4k_reg_write(e4k, E4K_REG_SYNTH5, p->x >> 8); + + /* we're in auto calibration mode, so there's no need to trigger it */ + + memcpy(&e4k->vco, p, sizeof(e4k->vco)); + + /* set the band */ + if (e4k->vco.flo < MHZ(140)) + e4k_band_set(e4k, E4K_BAND_VHF2); + else if (e4k->vco.flo < MHZ(350)) + e4k_band_set(e4k, E4K_BAND_VHF3); + else if (e4k->vco.flo < MHZ(1000)) + e4k_band_set(e4k, E4K_BAND_UHF); + else + e4k_band_set(e4k, E4K_BAND_L); + + /* select and set proper RF filter */ + e4k_rf_filter_set(e4k); + + return e4k->vco.flo; +} + +/*! \brief High-level tuning API, just specify frquency + * + * This function will compute matching PLL parameters, program them into the + * hardware and set the band as well as RF filter. + * + * \param[in] e4k reference to tuner + * \param[in] freq frequency in Hz + * \returns actual tuned frequency, negative in case of error + */ +int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) +{ + int rc, i; + struct e4k_pll_params p; + + /* determine PLL parameters */ + rc = e4k_compute_pll_params(&p, e4k->vco.fosc, freq); + if (rc < 0) + return rc; + + /* actually tune to those parameters */ + rc = e4k_tune_params(e4k, &p); + + /* check PLL lock */ + rc = e4k_reg_read(e4k, E4K_REG_SYNTH1); + if (!(rc & 0x01)) { + printf("[E4K] PLL not locked!\n"); + return -1; + } + + return 0; +} + +/*********************************************************************** + * Gain Control */ + +static const int8_t if_stage1_gain[] = { + -3, 6 +}; + +static const int8_t if_stage23_gain[] = { + 0, 3, 6, 9 +}; + +static const int8_t if_stage4_gain[] = { + 0, 1, 2, 2 +}; + +static const int8_t if_stage56_gain[] = { + 3, 6, 9, 12, 15, 15, 15, 15 +}; + +static const int8_t *if_stage_gain[] = { + 0, + if_stage1_gain, + if_stage23_gain, + if_stage23_gain, + if_stage4_gain, + if_stage56_gain, + if_stage56_gain +}; + +static const uint8_t if_stage_gain_len[] = { + 0, + ARRAY_SIZE(if_stage1_gain), + ARRAY_SIZE(if_stage23_gain), + ARRAY_SIZE(if_stage23_gain), + ARRAY_SIZE(if_stage4_gain), + ARRAY_SIZE(if_stage56_gain), + ARRAY_SIZE(if_stage56_gain) +}; + +static const struct reg_field if_stage_gain_regs[] = { + { E4K_REG_GAIN3, 0, 1 }, + { E4K_REG_GAIN3, 1, 2 }, + { E4K_REG_GAIN3, 3, 2 }, + { E4K_REG_GAIN3, 5, 2 }, + { E4K_REG_GAIN4, 0, 3 }, + { E4K_REG_GAIN4, 3, 3 } +}; + +static const int32_t lnagain[] = { + -50, 0, + -25, 1, + 0, 4, + 25, 5, + 50, 6, + 75, 7, + 100, 8, + 125, 9, + 150, 10, + 175, 11, + 200, 12, + 250, 13, + 300, 14, +}; + +static const int32_t enhgain[] = { + 10, 30, 50, 70 +}; + +int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain) +{ + uint32_t i; + for(i = 0; i < ARRAY_SIZE(lnagain)/2; ++i) { + if(lnagain[i*2] == gain) { + e4k_reg_set_mask(e4k, E4K_REG_GAIN1, 0xf, lnagain[i*2+1]); + return gain; + } + } + return -EINVAL; +} + +int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain) +{ + uint32_t i; + for(i = 0; i < ARRAY_SIZE(enhgain); ++i) { + if(enhgain[i] == gain) { + e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, E4K_AGC11_LNA_GAIN_ENH | (i << 1)); + return gain; + } + } + e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); + return 0; +} + +int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual) +{ + if (manual) { + /* Set LNA mode to manual */ + e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_SERIAL); + + /* Set Mixer Gain Control to manual */ + e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); + } else { + /* Set LNA mode to auto */ + e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_IF_SERIAL_LNA_AUTON); + /* Set Mixer Gain Control to auto */ + e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 1); + + e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); + } + + return 0; +} + +static int find_stage_gain(uint8_t stage, int8_t val) +{ + const int8_t *arr; + int i; + + if (stage >= ARRAY_SIZE(if_stage_gain)) + return -EINVAL; + + arr = if_stage_gain[stage]; + + for (i = 0; i < if_stage_gain_len[stage]; i++) { + if (arr[i] == val) + return i; + } + return -EINVAL; +} + +/*! \brief Set the gain of one of the IF gain stages + * \param[e4k] handle to the tuner chip + * \param [stage] numbere of the stage (1..6) + * \param [value] gain value in dBm + * \returns 0 on success, negative in case of error + */ +int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value) +{ + int rc; + uint8_t mask; + const struct reg_field *field; + + rc = find_stage_gain(stage, value); + if (rc < 0) + return rc; + + /* compute the bit-mask for the given gain field */ + field = &if_stage_gain_regs[stage]; + mask = width2mask[field->width] << field->shift; + + return e4k_reg_set_mask(e4k, field->reg, mask, rc << field->shift); +} + +int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value) +{ + uint8_t bit; + + switch (value) { + case 4: + bit = 0; + break; + case 12: + bit = 1; + break; + default: + return -EINVAL; + } + + return e4k_reg_set_mask(e4k, E4K_REG_GAIN2, 1, bit); +} + +int e4k_commonmode_set(struct e4k_state *e4k, int8_t value) +{ + if(value < 0) + return -EINVAL; + else if(value > 7) + return -EINVAL; + + return e4k_reg_set_mask(e4k, E4K_REG_DC7, 7, value); +} + +/*********************************************************************** + * DC Offset */ + +int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange) +{ + int res; + + if((iofs < 0x00) || (iofs > 0x3f)) + return -EINVAL; + if((irange < 0x00) || (irange > 0x03)) + return -EINVAL; + if((qofs < 0x00) || (qofs > 0x3f)) + return -EINVAL; + if((qrange < 0x00) || (qrange > 0x03)) + return -EINVAL; + + res = e4k_reg_set_mask(e4k, E4K_REG_DC2, 0x3f, iofs); + if(res < 0) + return res; + + res = e4k_reg_set_mask(e4k, E4K_REG_DC3, 0x3f, qofs); + if(res < 0) + return res; + + res = e4k_reg_set_mask(e4k, E4K_REG_DC4, 0x33, (qrange << 4) | irange); + return res; +} + +/*! \brief Perform a DC offset calibration right now + * \param[e4k] handle to the tuner chip + */ +int e4k_dc_offset_calibrate(struct e4k_state *e4k) +{ + /* make sure the DC range detector is enabled */ + e4k_reg_set_mask(e4k, E4K_REG_DC5, E4K_DC5_RANGE_DET_EN, E4K_DC5_RANGE_DET_EN); + + return e4k_reg_write(e4k, E4K_REG_DC1, 0x01); +} + + +static const int8_t if_gains_max[] = { + 0, 6, 9, 9, 2, 15, 15 +}; + +struct gain_comb { + int8_t mixer_gain; + int8_t if1_gain; + uint8_t reg; +}; + +static const struct gain_comb dc_gain_comb[] = { + { 4, -3, 0x50 }, + { 4, 6, 0x51 }, + { 12, -3, 0x52 }, + { 12, 6, 0x53 }, +}; + +#define TO_LUT(offset, range) (offset | (range << 6)) + +int e4k_dc_offset_gen_table(struct e4k_state *e4k) +{ + uint32_t i; + + /* FIXME: read ont current gain values and write them back + * before returning to the caller */ + + /* disable auto mixer gain */ + e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); + + /* set LNA/IF gain to full manual */ + e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, + E4K_AGC_MOD_SERIAL); + + /* set all 'other' gains to maximum */ + for (i = 2; i <= 6; i++) + e4k_if_gain_set(e4k, i, if_gains_max[i]); + + /* iterate over all mixer + if_stage_1 gain combinations */ + for (i = 0; i < ARRAY_SIZE(dc_gain_comb); i++) { + uint8_t offs_i, offs_q, range, range_i, range_q; + + /* set the combination of mixer / if1 gain */ + e4k_mixer_gain_set(e4k, dc_gain_comb[i].mixer_gain); + e4k_if_gain_set(e4k, 1, dc_gain_comb[i].if1_gain); + + /* perform actual calibration */ + e4k_dc_offset_calibrate(e4k); + + /* extract I/Q offset and range values */ + offs_i = e4k_reg_read(e4k, E4K_REG_DC2) & 0x3f; + offs_q = e4k_reg_read(e4k, E4K_REG_DC3) & 0x3f; + range = e4k_reg_read(e4k, E4K_REG_DC4); + range_i = range & 0x3; + range_q = (range >> 4) & 0x3; + + printf("Table %u I=%u/%u, Q=%u/%u\n", + i, range_i, offs_i, range_q, offs_q); + + /* write into the table */ + e4k_reg_write(e4k, dc_gain_comb[i].reg, + TO_LUT(offs_q, range_q)); + e4k_reg_write(e4k, dc_gain_comb[i].reg + 0x10, + TO_LUT(offs_i, range_i)); + } + + return 0; +} + +/*********************************************************************** + * Initialization */ + +static int magic_init(struct e4k_state *e4k) +{ + e4k_reg_write(e4k, 0x7e, 0x01); + e4k_reg_write(e4k, 0x7f, 0xfe); + e4k_reg_write(e4k, 0x82, 0x00); + e4k_reg_write(e4k, 0x86, 0x50); /* polarity A */ + e4k_reg_write(e4k, 0x87, 0x20); + e4k_reg_write(e4k, 0x88, 0x01); + e4k_reg_write(e4k, 0x9f, 0x7f); + e4k_reg_write(e4k, 0xa0, 0x07); + + return 0; +} + +/*! \brief Initialize the E4K tuner + */ +int e4k_init(struct e4k_state *e4k) +{ + /* make a dummy i2c read or write command, will not be ACKed! */ + e4k_reg_read(e4k, 0); + + /* Make sure we reset everything and clear POR indicator */ + e4k_reg_write(e4k, E4K_REG_MASTER1, + E4K_MASTER1_RESET | + E4K_MASTER1_NORM_STBY | + E4K_MASTER1_POR_DET + ); + + /* Configure clock input */ + e4k_reg_write(e4k, E4K_REG_CLK_INP, 0x00); + + /* Disable clock output */ + e4k_reg_write(e4k, E4K_REG_REF_CLK, 0x00); + e4k_reg_write(e4k, E4K_REG_CLKOUT_PWDN, 0x96); + + /* Write some magic values into registers */ + magic_init(e4k); +#if 0 + /* Set common mode voltage a bit higher for more margin 850 mv */ + e4k_commonmode_set(e4k, 4); + + /* Initialize DC offset lookup tables */ + e4k_dc_offset_gen_table(e4k); + + /* Enable time variant DC correction */ + e4k_reg_write(e4k, E4K_REG_DCTIME1, 0x01); + e4k_reg_write(e4k, E4K_REG_DCTIME2, 0x01); +#endif + + /* Set LNA mode to manual */ + e4k_reg_write(e4k, E4K_REG_AGC4, 0x10); /* High threshold */ + e4k_reg_write(e4k, E4K_REG_AGC5, 0x04); /* Low threshold */ + e4k_reg_write(e4k, E4K_REG_AGC6, 0x1a); /* LNA calib + loop rate */ + + e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, + E4K_AGC_MOD_SERIAL); + + /* Set Mixer Gain Control to manual */ + e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); + +#if 0 + /* Enable LNA Gain enhancement */ + e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, + E4K_AGC11_LNA_GAIN_ENH | (2 << 1)); + + /* Enable automatic IF gain mode switching */ + e4k_reg_set_mask(e4k, E4K_REG_AGC8, 0x1, E4K_AGC8_SENS_LIN_AUTO); +#endif + + /* Use auto-gain as default */ + e4k_enable_manual_gain(e4k, 0); + + /* Select moderate gain levels */ + e4k_if_gain_set(e4k, 1, 6); + e4k_if_gain_set(e4k, 2, 0); + e4k_if_gain_set(e4k, 3, 0); + e4k_if_gain_set(e4k, 4, 0); + e4k_if_gain_set(e4k, 5, 9); + e4k_if_gain_set(e4k, 6, 9); + + /* Set the most narrow filter we can possibly use */ + e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_MIX, KHZ(1900)); + e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_RC, KHZ(1000)); + e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_CHAN, KHZ(2150)); + e4k_if_filter_chan_enable(e4k, 1); + + /* Disable time variant DC correction and LUT */ + e4k_reg_set_mask(e4k, E4K_REG_DC5, 0x03, 0); + e4k_reg_set_mask(e4k, E4K_REG_DCTIME1, 0x03, 0); + e4k_reg_set_mask(e4k, E4K_REG_DCTIME2, 0x03, 0); + + return 0; +}