mirror of
https://github.com/rtlsdrblog/rtl-sdr-blog.git
synced 2024-11-10 04:37:37 +01:00
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 <steve@steve-m.de>
This commit is contained in:
parent
0af094070f
commit
86c34428aa
@ -1,5 +1,5 @@
|
|||||||
rtlsdr_HEADERS = rtl-sdr.h rtl-sdr_export.h
|
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)
|
rtlsdrdir = $(includedir)
|
||||||
|
60
include/reg_field.h
Normal file
60
include/reg_field.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef _REG_FIELD_H
|
||||||
|
#define _REG_FIELD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
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
|
@ -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_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 */
|
/* 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);
|
RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
|
||||||
|
|
||||||
|
@ -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
|
|
219
include/tuner_e4k.h
Normal file
219
include/tuner_e4k.h
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#ifndef _E4K_TUNER_H
|
||||||
|
#define _E4K_TUNER_H
|
||||||
|
|
||||||
|
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 */
|
@ -22,7 +22,7 @@
|
|||||||
########################################################################
|
########################################################################
|
||||||
add_library(rtlsdr_shared SHARED
|
add_library(rtlsdr_shared SHARED
|
||||||
rtl-sdr.c
|
rtl-sdr.c
|
||||||
tuner_e4000.c
|
tuner_e4k.c
|
||||||
tuner_fc0012.c
|
tuner_fc0012.c
|
||||||
tuner_fc0013.c
|
tuner_fc0013.c
|
||||||
tuner_fc2580.c
|
tuner_fc2580.c
|
||||||
@ -37,7 +37,7 @@ set_target_properties(rtlsdr_shared PROPERTIES OUTPUT_NAME rtlsdr)
|
|||||||
|
|
||||||
add_library(rtlsdr_static STATIC
|
add_library(rtlsdr_static STATIC
|
||||||
rtl-sdr.c
|
rtl-sdr.c
|
||||||
tuner_e4000.c
|
tuner_e4k.c
|
||||||
tuner_fc0012.c
|
tuner_fc0012.c
|
||||||
tuner_fc0013.c
|
tuner_fc0013.c
|
||||||
tuner_fc2580.c
|
tuner_fc2580.c
|
||||||
|
@ -7,7 +7,7 @@ AM_CFLAGS = -fPIC -Wall
|
|||||||
|
|
||||||
lib_LTLIBRARIES = librtlsdr.la
|
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)
|
librtlsdr_la_LDFALGS = -version-info $(LIBVERSION)
|
||||||
|
|
||||||
bin_PROGRAMS = rtl_sdr rtl_tcp
|
bin_PROGRAMS = rtl_sdr rtl_tcp
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
@ -40,7 +41,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "rtl-sdr.h"
|
#include "rtl-sdr.h"
|
||||||
#include "tuner_e4000.h"
|
#include "tuner_e4k.h"
|
||||||
#include "tuner_fc0012.h"
|
#include "tuner_fc0012.h"
|
||||||
#include "tuner_fc0013.h"
|
#include "tuner_fc0013.h"
|
||||||
#include "tuner_fc2580.h"
|
#include "tuner_fc2580.h"
|
||||||
@ -52,6 +53,7 @@ typedef struct rtlsdr_tuner {
|
|||||||
int (*set_freq)(void *, uint32_t freq /* Hz */);
|
int (*set_freq)(void *, uint32_t freq /* Hz */);
|
||||||
int (*set_bw)(void *, int bw /* Hz */);
|
int (*set_bw)(void *, int bw /* Hz */);
|
||||||
int (*set_gain)(void *, int gain /* dB */);
|
int (*set_gain)(void *, int gain /* dB */);
|
||||||
|
int (*set_gain_mode)(void *, int manual);
|
||||||
} rtlsdr_tuner_t;
|
} rtlsdr_tuner_t;
|
||||||
|
|
||||||
enum rtlsdr_async_status {
|
enum rtlsdr_async_status {
|
||||||
@ -79,20 +81,48 @@ struct rtlsdr_dev {
|
|||||||
uint32_t freq; /* Hz */
|
uint32_t freq; /* Hz */
|
||||||
int corr; /* ppm */
|
int corr; /* ppm */
|
||||||
int gain; /* dB */
|
int gain; /* dB */
|
||||||
|
struct e4k_state e4k_s;
|
||||||
};
|
};
|
||||||
|
|
||||||
void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val);
|
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 */
|
/* generic tuner interface functions, shall be moved to the tuner implementations */
|
||||||
int e4k_init(void *dev) { return e4000_Initialize(dev); }
|
int e4000_init(void *dev) {
|
||||||
int e4k_exit(void *dev) { return 0; }
|
rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev;
|
||||||
int e4k_set_freq(void *dev, uint32_t freq) {
|
devt->e4k_s.i2c_addr = E4K_I2C_ADDR;
|
||||||
return e4000_SetRfFreqHz(dev, freq);
|
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) {
|
int e4000_exit(void *dev) { return 0; }
|
||||||
return e4000_SetBandwidthHz(dev, 4000000);
|
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_init(void *dev) { return FC0012_Open(dev); }
|
||||||
int fc0012_exit(void *dev) { return 0; }
|
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);
|
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(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_init(void *dev) { return FC0013_Open(dev); }
|
||||||
int fc0013_exit(void *dev) { return 0; }
|
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);
|
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(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_init(void *dev) { return fc2580_Initialize(dev); }
|
||||||
int fc2580_exit(void *dev) { return 0; }
|
int fc2580_exit(void *dev) { return 0; }
|
||||||
@ -125,6 +157,7 @@ int fc2580_set_bw(void *dev, int bw) {
|
|||||||
return fc2580_SetBandwidthMode(dev, 1);
|
return fc2580_SetBandwidthMode(dev, 1);
|
||||||
}
|
}
|
||||||
int fc2580_set_gain(void *dev, int gain) { return 0; }
|
int fc2580_set_gain(void *dev, int gain) { return 0; }
|
||||||
|
int fc2580_set_gain_mode(void *dev, int manual) { return 0; }
|
||||||
|
|
||||||
enum rtlsdr_tuners {
|
enum rtlsdr_tuners {
|
||||||
RTLSDR_TUNER_E4000,
|
RTLSDR_TUNER_E4000,
|
||||||
@ -135,20 +168,24 @@ enum rtlsdr_tuners {
|
|||||||
|
|
||||||
static rtlsdr_tuner_t tuners[] = {
|
static rtlsdr_tuner_t tuners[] = {
|
||||||
{
|
{
|
||||||
e4k_init, e4k_exit,
|
e4000_init, e4000_exit,
|
||||||
e4k_set_freq, e4k_set_bw, e4k_set_gain
|
e4000_set_freq, e4000_set_bw, e4000_set_gain,
|
||||||
|
e4000_set_gain_mode
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fc0012_init, fc0012_exit,
|
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_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_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;
|
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)
|
int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len)
|
||||||
{
|
{
|
||||||
uint16_t addr = i2c_addr;
|
uint16_t addr = i2c_addr;
|
||||||
@ -594,8 +641,11 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain)
|
|||||||
if (!dev || !dev->tuner)
|
if (!dev || !dev->tuner)
|
||||||
return -1;
|
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);
|
r = dev->tuner->set_gain((void *)dev, gain);
|
||||||
|
rtlsdr_set_i2c_repeater(dev, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!r)
|
if (!r)
|
||||||
dev->gain = gain;
|
dev->gain = gain;
|
||||||
@ -611,6 +661,24 @@ int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev)
|
|||||||
return dev->gain;
|
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)
|
int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
|
||||||
{
|
{
|
||||||
uint16_t tmp;
|
uint16_t tmp;
|
||||||
@ -862,8 +930,6 @@ err:
|
|||||||
|
|
||||||
int rtlsdr_close(rtlsdr_dev_t *dev)
|
int rtlsdr_close(rtlsdr_dev_t *dev)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#else
|
||||||
|
#include <WinSock2.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@ -278,6 +280,9 @@ static void *command_worker(void *arg)
|
|||||||
printf("set freq %d\n", cmd.param);
|
printf("set freq %d\n", cmd.param);
|
||||||
rtlsdr_set_center_freq(dev, cmd.param);
|
rtlsdr_set_center_freq(dev, cmd.param);
|
||||||
break;
|
break;
|
||||||
|
case 0x04:
|
||||||
|
rtlsdr_set_tuner_gain(dev, cmd.param);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -293,7 +298,7 @@ int main(int argc, char **argv)
|
|||||||
uint32_t frequency = 0, samp_rate = 2048000;
|
uint32_t frequency = 0, samp_rate = 2048000;
|
||||||
struct sockaddr_in local, remote;
|
struct sockaddr_in local, remote;
|
||||||
int device_count;
|
int device_count;
|
||||||
uint32_t dev_index = 0, gain = 5;
|
uint32_t dev_index = 0, gain = -10;
|
||||||
struct llist *curelem,*prev;
|
struct llist *curelem,*prev;
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
void *status;
|
void *status;
|
||||||
|
2067
src/tuner_e4000.c
2067
src/tuner_e4000.c
File diff suppressed because it is too large
Load Diff
955
src/tuner_e4k.c
Normal file
955
src/tuner_e4k.c
Normal file
@ -0,0 +1,955 @@
|
|||||||
|
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <reg_field.h>
|
||||||
|
#include <tuner_e4k.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user