|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator | 
|  | * | 
|  | * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) | 
|  | */ | 
|  | #include <linux/init.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/jiffies.h> | 
|  | #include <asm/div64.h> | 
|  |  | 
|  | #include <media/dvb_frontend.h> | 
|  | #include "si21xx.h" | 
|  |  | 
|  | #define	REVISION_REG			0x00 | 
|  | #define	SYSTEM_MODE_REG			0x01 | 
|  | #define	TS_CTRL_REG_1			0x02 | 
|  | #define	TS_CTRL_REG_2			0x03 | 
|  | #define	PIN_CTRL_REG_1			0x04 | 
|  | #define	PIN_CTRL_REG_2			0x05 | 
|  | #define	LOCK_STATUS_REG_1		0x0f | 
|  | #define	LOCK_STATUS_REG_2		0x10 | 
|  | #define	ACQ_STATUS_REG			0x11 | 
|  | #define	ACQ_CTRL_REG_1			0x13 | 
|  | #define	ACQ_CTRL_REG_2			0x14 | 
|  | #define	PLL_DIVISOR_REG			0x15 | 
|  | #define	COARSE_TUNE_REG			0x16 | 
|  | #define	FINE_TUNE_REG_L			0x17 | 
|  | #define	FINE_TUNE_REG_H			0x18 | 
|  |  | 
|  | #define	ANALOG_AGC_POWER_LEVEL_REG	0x28 | 
|  | #define	CFO_ESTIMATOR_CTRL_REG_1	0x29 | 
|  | #define	CFO_ESTIMATOR_CTRL_REG_2	0x2a | 
|  | #define	CFO_ESTIMATOR_CTRL_REG_3	0x2b | 
|  |  | 
|  | #define	SYM_RATE_ESTIMATE_REG_L		0x31 | 
|  | #define	SYM_RATE_ESTIMATE_REG_M		0x32 | 
|  | #define	SYM_RATE_ESTIMATE_REG_H		0x33 | 
|  |  | 
|  | #define	CFO_ESTIMATOR_OFFSET_REG_L	0x36 | 
|  | #define	CFO_ESTIMATOR_OFFSET_REG_H	0x37 | 
|  | #define	CFO_ERROR_REG_L			0x38 | 
|  | #define	CFO_ERROR_REG_H			0x39 | 
|  | #define	SYM_RATE_ESTIMATOR_CTRL_REG	0x3a | 
|  |  | 
|  | #define	SYM_RATE_REG_L			0x3f | 
|  | #define	SYM_RATE_REG_M			0x40 | 
|  | #define	SYM_RATE_REG_H			0x41 | 
|  | #define	SYM_RATE_ESTIMATOR_MAXIMUM_REG	0x42 | 
|  | #define	SYM_RATE_ESTIMATOR_MINIMUM_REG	0x43 | 
|  |  | 
|  | #define	C_N_ESTIMATOR_CTRL_REG		0x7c | 
|  | #define	C_N_ESTIMATOR_THRSHLD_REG	0x7d | 
|  | #define	C_N_ESTIMATOR_LEVEL_REG_L	0x7e | 
|  | #define	C_N_ESTIMATOR_LEVEL_REG_H	0x7f | 
|  |  | 
|  | #define	BLIND_SCAN_CTRL_REG		0x80 | 
|  |  | 
|  | #define	LSA_CTRL_REG_1			0x8D | 
|  | #define	SPCTRM_TILT_CORR_THRSHLD_REG	0x8f | 
|  | #define	ONE_DB_BNDWDTH_THRSHLD_REG	0x90 | 
|  | #define	TWO_DB_BNDWDTH_THRSHLD_REG	0x91 | 
|  | #define	THREE_DB_BNDWDTH_THRSHLD_REG	0x92 | 
|  | #define	INBAND_POWER_THRSHLD_REG	0x93 | 
|  | #define	REF_NOISE_LVL_MRGN_THRSHLD_REG	0x94 | 
|  |  | 
|  | #define	VIT_SRCH_CTRL_REG_1		0xa0 | 
|  | #define	VIT_SRCH_CTRL_REG_2		0xa1 | 
|  | #define	VIT_SRCH_CTRL_REG_3		0xa2 | 
|  | #define	VIT_SRCH_STATUS_REG		0xa3 | 
|  | #define	VITERBI_BER_COUNT_REG_L		0xab | 
|  | #define	REED_SOLOMON_CTRL_REG		0xb0 | 
|  | #define	REED_SOLOMON_ERROR_COUNT_REG_L	0xb1 | 
|  | #define	PRBS_CTRL_REG			0xb5 | 
|  |  | 
|  | #define	LNB_CTRL_REG_1			0xc0 | 
|  | #define	LNB_CTRL_REG_2			0xc1 | 
|  | #define	LNB_CTRL_REG_3			0xc2 | 
|  | #define	LNB_CTRL_REG_4			0xc3 | 
|  | #define	LNB_CTRL_STATUS_REG		0xc4 | 
|  | #define	LNB_FIFO_REGS_0			0xc5 | 
|  | #define	LNB_FIFO_REGS_1			0xc6 | 
|  | #define	LNB_FIFO_REGS_2			0xc7 | 
|  | #define	LNB_FIFO_REGS_3			0xc8 | 
|  | #define	LNB_FIFO_REGS_4			0xc9 | 
|  | #define	LNB_FIFO_REGS_5			0xca | 
|  | #define	LNB_SUPPLY_CTRL_REG_1		0xcb | 
|  | #define	LNB_SUPPLY_CTRL_REG_2		0xcc | 
|  | #define	LNB_SUPPLY_CTRL_REG_3		0xcd | 
|  | #define	LNB_SUPPLY_CTRL_REG_4		0xce | 
|  | #define	LNB_SUPPLY_STATUS_REG		0xcf | 
|  |  | 
|  | #define FAIL	-1 | 
|  | #define PASS	0 | 
|  |  | 
|  | #define ALLOWABLE_FS_COUNT	10 | 
|  | #define STATUS_BER		0 | 
|  | #define STATUS_UCBLOCKS		1 | 
|  |  | 
|  | static int debug; | 
|  | #define dprintk(args...) \ | 
|  | do { \ | 
|  | if (debug) \ | 
|  | printk(KERN_DEBUG "si21xx: " args); \ | 
|  | } while (0) | 
|  |  | 
|  | enum { | 
|  | ACTIVE_HIGH, | 
|  | ACTIVE_LOW | 
|  | }; | 
|  | enum { | 
|  | BYTE_WIDE, | 
|  | BIT_WIDE | 
|  | }; | 
|  | enum { | 
|  | CLK_GAPPED_MODE, | 
|  | CLK_CONTINUOUS_MODE | 
|  | }; | 
|  | enum { | 
|  | RISING_EDGE, | 
|  | FALLING_EDGE | 
|  | }; | 
|  | enum { | 
|  | MSB_FIRST, | 
|  | LSB_FIRST | 
|  | }; | 
|  | enum { | 
|  | SERIAL, | 
|  | PARALLEL | 
|  | }; | 
|  |  | 
|  | struct si21xx_state { | 
|  | struct i2c_adapter *i2c; | 
|  | const struct si21xx_config *config; | 
|  | struct dvb_frontend frontend; | 
|  | u8 initialised:1; | 
|  | int errmode; | 
|  | int fs;			/*Sampling rate of the ADC in MHz*/ | 
|  | }; | 
|  |  | 
|  | /*	register default initialization */ | 
|  | static u8 serit_sp1511lhb_inittab[] = { | 
|  | 0x01, 0x28,	/* set i2c_inc_disable */ | 
|  | 0x20, 0x03, | 
|  | 0x27, 0x20, | 
|  | 0xe0, 0x45, | 
|  | 0xe1, 0x08, | 
|  | 0xfe, 0x01, | 
|  | 0x01, 0x28, | 
|  | 0x89, 0x09, | 
|  | 0x04, 0x80, | 
|  | 0x05, 0x01, | 
|  | 0x06, 0x00, | 
|  | 0x20, 0x03, | 
|  | 0x24, 0x88, | 
|  | 0x29, 0x09, | 
|  | 0x2a, 0x0f, | 
|  | 0x2c, 0x10, | 
|  | 0x2d, 0x19, | 
|  | 0x2e, 0x08, | 
|  | 0x2f, 0x10, | 
|  | 0x30, 0x19, | 
|  | 0x34, 0x20, | 
|  | 0x35, 0x03, | 
|  | 0x45, 0x02, | 
|  | 0x46, 0x45, | 
|  | 0x47, 0xd0, | 
|  | 0x48, 0x00, | 
|  | 0x49, 0x40, | 
|  | 0x4a, 0x03, | 
|  | 0x4c, 0xfd, | 
|  | 0x4f, 0x2e, | 
|  | 0x50, 0x2e, | 
|  | 0x51, 0x10, | 
|  | 0x52, 0x10, | 
|  | 0x56, 0x92, | 
|  | 0x59, 0x00, | 
|  | 0x5a, 0x2d, | 
|  | 0x5b, 0x33, | 
|  | 0x5c, 0x1f, | 
|  | 0x5f, 0x76, | 
|  | 0x62, 0xc0, | 
|  | 0x63, 0xc0, | 
|  | 0x64, 0xf3, | 
|  | 0x65, 0xf3, | 
|  | 0x79, 0x40, | 
|  | 0x6a, 0x40, | 
|  | 0x6b, 0x0a, | 
|  | 0x6c, 0x80, | 
|  | 0x6d, 0x27, | 
|  | 0x71, 0x06, | 
|  | 0x75, 0x60, | 
|  | 0x78, 0x00, | 
|  | 0x79, 0xb5, | 
|  | 0x7c, 0x05, | 
|  | 0x7d, 0x1a, | 
|  | 0x87, 0x55, | 
|  | 0x88, 0x72, | 
|  | 0x8f, 0x08, | 
|  | 0x90, 0xe0, | 
|  | 0x94, 0x40, | 
|  | 0xa0, 0x3f, | 
|  | 0xa1, 0xc0, | 
|  | 0xa4, 0xcc, | 
|  | 0xa5, 0x66, | 
|  | 0xa6, 0x66, | 
|  | 0xa7, 0x7b, | 
|  | 0xa8, 0x7b, | 
|  | 0xa9, 0x7b, | 
|  | 0xaa, 0x9a, | 
|  | 0xed, 0x04, | 
|  | 0xad, 0x00, | 
|  | 0xae, 0x03, | 
|  | 0xcc, 0xab, | 
|  | 0x01, 0x08, | 
|  | 0xff, 0xff | 
|  | }; | 
|  |  | 
|  | /*	low level read/writes */ | 
|  | static int si21_writeregs(struct si21xx_state *state, u8 reg1, | 
|  | u8 *data, int len) | 
|  | { | 
|  | int ret; | 
|  | u8 buf[60];/* = { reg1, data };*/ | 
|  | struct i2c_msg msg = { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = 0, | 
|  | .buf = buf, | 
|  | .len = len + 1 | 
|  | }; | 
|  |  | 
|  | if (len > sizeof(buf) - 1) | 
|  | return -EINVAL; | 
|  |  | 
|  | msg.buf[0] =  reg1; | 
|  | memcpy(msg.buf + 1, data, len); | 
|  |  | 
|  | ret = i2c_transfer(state->i2c, &msg, 1); | 
|  |  | 
|  | if (ret != 1) | 
|  | dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, ret == %i)\n", | 
|  | __func__, reg1, data[0], ret); | 
|  |  | 
|  | return (ret != 1) ? -EREMOTEIO : 0; | 
|  | } | 
|  |  | 
|  | static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) | 
|  | { | 
|  | int ret; | 
|  | u8 buf[] = { reg, data }; | 
|  | struct i2c_msg msg = { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = 0, | 
|  | .buf = buf, | 
|  | .len = 2 | 
|  | }; | 
|  |  | 
|  | ret = i2c_transfer(state->i2c, &msg, 1); | 
|  |  | 
|  | if (ret != 1) | 
|  | dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, ret == %i)\n", | 
|  | __func__, reg, data, ret); | 
|  |  | 
|  | return (ret != 1) ? -EREMOTEIO : 0; | 
|  | } | 
|  |  | 
|  | static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | if (len != 2) | 
|  | return -EINVAL; | 
|  |  | 
|  | return si21_writereg(state, buf[0], buf[1]); | 
|  | } | 
|  |  | 
|  | static u8 si21_readreg(struct si21xx_state *state, u8 reg) | 
|  | { | 
|  | int ret; | 
|  | u8 b0[] = { reg }; | 
|  | u8 b1[] = { 0 }; | 
|  | struct i2c_msg msg[] = { | 
|  | { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = 0, | 
|  | .buf = b0, | 
|  | .len = 1 | 
|  | }, { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = I2C_M_RD, | 
|  | .buf = b1, | 
|  | .len = 1 | 
|  | } | 
|  | }; | 
|  |  | 
|  | ret = i2c_transfer(state->i2c, msg, 2); | 
|  |  | 
|  | if (ret != 2) | 
|  | dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", | 
|  | __func__, reg, ret); | 
|  |  | 
|  | return b1[0]; | 
|  | } | 
|  |  | 
|  | static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) | 
|  | { | 
|  | int ret; | 
|  | struct i2c_msg msg[] = { | 
|  | { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = 0, | 
|  | .buf = ®1, | 
|  | .len = 1 | 
|  | }, { | 
|  | .addr = state->config->demod_address, | 
|  | .flags = I2C_M_RD, | 
|  | .buf = b, | 
|  | .len = len | 
|  | } | 
|  | }; | 
|  |  | 
|  | ret = i2c_transfer(state->i2c, msg, 2); | 
|  |  | 
|  | if (ret != 2) | 
|  | dprintk("%s: readreg error (ret == %i)\n", __func__, ret); | 
|  |  | 
|  | return ret == 2 ? 0 : -1; | 
|  | } | 
|  |  | 
|  | static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) | 
|  | { | 
|  | unsigned long start = jiffies; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { | 
|  | if (jiffies - start > timeout) { | 
|  | dprintk("%s: timeout!!\n", __func__); | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  | msleep(10); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u32 sym_rate, data_rate; | 
|  | int i; | 
|  | u8 sym_rate_bytes[3]; | 
|  |  | 
|  | dprintk("%s : srate = %i\n", __func__ , srate); | 
|  |  | 
|  | if ((srate < 1000000) || (srate > 45000000)) | 
|  | return -EINVAL; | 
|  |  | 
|  | data_rate = srate; | 
|  | sym_rate = 0; | 
|  |  | 
|  | for (i = 0; i < 4; ++i) { | 
|  | sym_rate /= 100; | 
|  | sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / | 
|  | state->fs; | 
|  | data_rate /= 100; | 
|  | } | 
|  | for (i = 0; i < 3; ++i) | 
|  | sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); | 
|  |  | 
|  | si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, | 
|  | struct dvb_diseqc_master_cmd *m) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 lnb_status; | 
|  | u8 LNB_CTRL_1; | 
|  | int status; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | status = PASS; | 
|  | LNB_CTRL_1 = 0; | 
|  |  | 
|  | status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); | 
|  | status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); | 
|  |  | 
|  | /*fill the FIFO*/ | 
|  | status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); | 
|  |  | 
|  | LNB_CTRL_1 = (lnb_status & 0x70); | 
|  | LNB_CTRL_1 |= m->msg_len; | 
|  |  | 
|  | LNB_CTRL_1 |= 0x80;	/* begin LNB signaling */ | 
|  |  | 
|  | status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, | 
|  | enum fe_sec_mini_cmd burst) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 val; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | if (si21xx_wait_diseqc_idle(state, 100) < 0) | 
|  | return -ETIMEDOUT; | 
|  |  | 
|  | val = (0x80 | si21_readreg(state, 0xc1)); | 
|  | if (si21_writereg(state, LNB_CTRL_REG_1, | 
|  | burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) | 
|  | return -EREMOTEIO; | 
|  |  | 
|  | if (si21xx_wait_diseqc_idle(state, 100) < 0) | 
|  | return -ETIMEDOUT; | 
|  |  | 
|  | if (si21_writereg(state, LNB_CTRL_REG_1, val)) | 
|  | return -EREMOTEIO; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | /*	30.06.2008 */ | 
|  | static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 val; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  | val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); | 
|  |  | 
|  | switch (tone) { | 
|  | case SEC_TONE_ON: | 
|  | return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); | 
|  |  | 
|  | case SEC_TONE_OFF: | 
|  | return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); | 
|  |  | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | u8 val; | 
|  | dprintk("%s: %s\n", __func__, | 
|  | volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : | 
|  | volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); | 
|  |  | 
|  |  | 
|  | val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); | 
|  |  | 
|  | switch (volt) { | 
|  | case SEC_VOLTAGE_18: | 
|  | return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); | 
|  | break; | 
|  | case SEC_VOLTAGE_13: | 
|  | return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int si21xx_init(struct dvb_frontend *fe) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | int i; | 
|  | int status = 0; | 
|  | u8 reg1; | 
|  | u8 val; | 
|  | u8 reg2[2]; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | for (i = 0; ; i += 2) { | 
|  | reg1 = serit_sp1511lhb_inittab[i]; | 
|  | val = serit_sp1511lhb_inittab[i+1]; | 
|  | if (reg1 == 0xff && val == 0xff) | 
|  | break; | 
|  | si21_writeregs(state, reg1, &val, 1); | 
|  | } | 
|  |  | 
|  | /*DVB QPSK SYSTEM MODE REG*/ | 
|  | reg1 = 0x08; | 
|  | si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); | 
|  |  | 
|  | /*transport stream config*/ | 
|  | /* | 
|  | mode = PARALLEL; | 
|  | sdata_form = LSB_FIRST; | 
|  | clk_edge = FALLING_EDGE; | 
|  | clk_mode = CLK_GAPPED_MODE; | 
|  | strt_len = BYTE_WIDE; | 
|  | sync_pol = ACTIVE_HIGH; | 
|  | val_pol = ACTIVE_HIGH; | 
|  | err_pol = ACTIVE_HIGH; | 
|  | sclk_rate = 0x00; | 
|  | parity = 0x00 ; | 
|  | data_delay = 0x00; | 
|  | clk_delay = 0x00; | 
|  | pclk_smooth = 0x00; | 
|  | */ | 
|  | reg2[0] = | 
|  | PARALLEL + (LSB_FIRST << 1) | 
|  | + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) | 
|  | + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) | 
|  | + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); | 
|  |  | 
|  | reg2[1] = 0; | 
|  | /*	sclk_rate + (parity << 2) | 
|  | + (data_delay << 3) + (clk_delay << 4) | 
|  | + (pclk_smooth << 5); | 
|  | */ | 
|  | status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); | 
|  | if (status != 0) | 
|  | dprintk(" %s : TS Set Error\n", __func__); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } | 
|  |  | 
|  | static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 regs_read[2]; | 
|  | u8 reg_read; | 
|  | u8 i; | 
|  | u8 lock; | 
|  | u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); | 
|  |  | 
|  | si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); | 
|  | reg_read = 0; | 
|  |  | 
|  | for (i = 0; i < 7; ++i) | 
|  | reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); | 
|  |  | 
|  | lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); | 
|  |  | 
|  | dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); | 
|  | *status = 0; | 
|  |  | 
|  | if (signal > 10) | 
|  | *status |= FE_HAS_SIGNAL; | 
|  |  | 
|  | if (lock & 0x2) | 
|  | *status |= FE_HAS_CARRIER; | 
|  |  | 
|  | if (lock & 0x20) | 
|  | *status |= FE_HAS_VITERBI; | 
|  |  | 
|  | if (lock & 0x40) | 
|  | *status |= FE_HAS_SYNC; | 
|  |  | 
|  | if ((lock & 0x7b) == 0x7b) | 
|  | *status |= FE_HAS_LOCK; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, | 
|  | (u8*)agclevel, 0x01);*/ | 
|  |  | 
|  | u16 signal = (3 * si21_readreg(state, 0x27) * | 
|  | si21_readreg(state, 0x28)); | 
|  |  | 
|  | dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, | 
|  | si21_readreg(state, 0x27), | 
|  | si21_readreg(state, 0x28), (int) signal); | 
|  |  | 
|  | signal  <<= 4; | 
|  | *strength = signal; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | if (state->errmode != STATUS_BER) | 
|  | return 0; | 
|  |  | 
|  | *ber = (si21_readreg(state, 0x1d) << 8) | | 
|  | si21_readreg(state, 0x1e); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | | 
|  | si21_readreg(state, 0x25)); | 
|  | xsnr = 3 * (xsnr - 0xa100); | 
|  | *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | if (state->errmode != STATUS_UCBLOCKS) | 
|  | *ucblocks = 0; | 
|  | else | 
|  | *ucblocks = (si21_readreg(state, 0x1d) << 8) | | 
|  | si21_readreg(state, 0x1e); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*	initiates a channel acquisition sequence | 
|  | using the specified symbol rate and code rate */ | 
|  | static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, | 
|  | enum fe_code_rate crate) | 
|  | { | 
|  |  | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 coderates[] = { | 
|  | 0x0, 0x01, 0x02, 0x04, 0x00, | 
|  | 0x8, 0x10, 0x20, 0x00, 0x3f | 
|  | }; | 
|  |  | 
|  | u8 coderate_ptr; | 
|  | int status; | 
|  | u8 start_acq = 0x80; | 
|  | u8 reg, regs[3]; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | status = PASS; | 
|  | coderate_ptr = coderates[crate]; | 
|  |  | 
|  | si21xx_set_symbolrate(fe, symbrate); | 
|  |  | 
|  | /* write code rates to use in the Viterbi search */ | 
|  | status |= si21_writeregs(state, | 
|  | VIT_SRCH_CTRL_REG_1, | 
|  | &coderate_ptr, 0x01); | 
|  |  | 
|  | /* clear acq_start bit */ | 
|  | status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); | 
|  | reg &= ~start_acq; | 
|  | status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); | 
|  |  | 
|  | /* use new Carrier Frequency Offset Estimator (QuickLock) */ | 
|  | regs[0] = 0xCB; | 
|  | regs[1] = 0x40; | 
|  | regs[2] = 0xCB; | 
|  |  | 
|  | status |= si21_writeregs(state, | 
|  | TWO_DB_BNDWDTH_THRSHLD_REG, | 
|  | ®s[0], 0x03); | 
|  | reg = 0x56; | 
|  | status |= si21_writeregs(state, | 
|  | LSA_CTRL_REG_1, ®, 1); | 
|  | reg = 0x05; | 
|  | status |= si21_writeregs(state, | 
|  | BLIND_SCAN_CTRL_REG, ®, 1); | 
|  | /* start automatic acq */ | 
|  | status |= si21_writeregs(state, | 
|  | ACQ_CTRL_REG_2, &start_acq, 0x01); | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | static int si21xx_set_frontend(struct dvb_frontend *fe) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | 
|  |  | 
|  | /* freq		Channel carrier frequency in KHz (i.e. 1550000 KHz) | 
|  | datarate	Channel symbol rate in Sps (i.e. 22500000 Sps)*/ | 
|  |  | 
|  | /* in MHz */ | 
|  | unsigned char coarse_tune_freq; | 
|  | int fine_tune_freq; | 
|  | unsigned char sample_rate = 0; | 
|  | /* boolean */ | 
|  | bool inband_interferer_ind; | 
|  |  | 
|  | /* INTERMEDIATE VALUES */ | 
|  | int icoarse_tune_freq; /* MHz */ | 
|  | int ifine_tune_freq; /* MHz */ | 
|  | unsigned int band_high; | 
|  | unsigned int band_low; | 
|  | unsigned int x1; | 
|  | unsigned int x2; | 
|  | int i; | 
|  | bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; | 
|  | bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; | 
|  | int status; | 
|  |  | 
|  | /* allowable sample rates for ADC in MHz */ | 
|  | int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, | 
|  | 196, 204, 205, 206, 207 | 
|  | }; | 
|  | /* in MHz */ | 
|  | int if_limit_high; | 
|  | int if_limit_low; | 
|  | int lnb_lo; | 
|  | int lnb_uncertanity; | 
|  |  | 
|  | int rf_freq; | 
|  | int data_rate; | 
|  | unsigned char regs[4]; | 
|  |  | 
|  | dprintk("%s : FE_SET_FRONTEND\n", __func__); | 
|  |  | 
|  | if (c->delivery_system != SYS_DVBS) { | 
|  | dprintk("%s: unsupported delivery system selected (%d)\n", | 
|  | __func__, c->delivery_system); | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) | 
|  | inband_interferer_div2[i] = inband_interferer_div4[i] = false; | 
|  |  | 
|  | if_limit_high = -700000; | 
|  | if_limit_low = -100000; | 
|  | /* in MHz */ | 
|  | lnb_lo = 0; | 
|  | lnb_uncertanity = 0; | 
|  |  | 
|  | rf_freq = 10 * c->frequency ; | 
|  | data_rate = c->symbol_rate / 100; | 
|  |  | 
|  | status = PASS; | 
|  |  | 
|  | band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) | 
|  | + (data_rate * 135)) / 200; | 
|  |  | 
|  | band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) | 
|  | + (data_rate * 135)) / 200; | 
|  |  | 
|  |  | 
|  | icoarse_tune_freq = 100000 * | 
|  | (((rf_freq - lnb_lo) - | 
|  | (if_limit_low + if_limit_high) / 2) | 
|  | / 100000); | 
|  |  | 
|  | ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; | 
|  |  | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | 
|  | x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * | 
|  | (afs[i] * 2500) + afs[i] * 2500; | 
|  |  | 
|  | x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * | 
|  | (afs[i] * 2500); | 
|  |  | 
|  | if (((band_low < x1) && (x1 < band_high)) || | 
|  | ((band_low < x2) && (x2 < band_high))) | 
|  | inband_interferer_div4[i] = true; | 
|  |  | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | 
|  | x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * | 
|  | (afs[i] * 5000) + afs[i] * 5000; | 
|  |  | 
|  | x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * | 
|  | (afs[i] * 5000); | 
|  |  | 
|  | if (((band_low < x1) && (x1 < band_high)) || | 
|  | ((band_low < x2) && (x2 < band_high))) | 
|  | inband_interferer_div2[i] = true; | 
|  | } | 
|  |  | 
|  | inband_interferer_ind = true; | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | 
|  | if (inband_interferer_div2[i] || inband_interferer_div4[i]) { | 
|  | inband_interferer_ind = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (inband_interferer_ind) { | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | 
|  | if (!inband_interferer_div2[i]) { | 
|  | sample_rate = (u8) afs[i]; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { | 
|  | if ((inband_interferer_div2[i] || | 
|  | !inband_interferer_div4[i])) { | 
|  | sample_rate = (u8) afs[i]; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | if (sample_rate > 207 || sample_rate < 192) | 
|  | sample_rate = 200; | 
|  |  | 
|  | fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / | 
|  | ((sample_rate) * 1000)); | 
|  |  | 
|  | coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); | 
|  |  | 
|  | regs[0] = sample_rate; | 
|  | regs[1] = coarse_tune_freq; | 
|  | regs[2] = fine_tune_freq & 0xFF; | 
|  | regs[3] = fine_tune_freq >> 8 & 0xFF; | 
|  |  | 
|  | status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); | 
|  |  | 
|  | state->fs = sample_rate;/*ADC MHz*/ | 
|  | si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int si21xx_sleep(struct dvb_frontend *fe) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  | u8 regdata; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); | 
|  | regdata |= 1 << 6; | 
|  | si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); | 
|  | state->initialised = 0; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void si21xx_release(struct dvb_frontend *fe) | 
|  | { | 
|  | struct si21xx_state *state = fe->demodulator_priv; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | kfree(state); | 
|  | } | 
|  |  | 
|  | static const struct dvb_frontend_ops si21xx_ops = { | 
|  | .delsys = { SYS_DVBS }, | 
|  | .info = { | 
|  | .name			= "SL SI21XX DVB-S", | 
|  | .frequency_min_hz	=  950 * MHz, | 
|  | .frequency_max_hz	= 2150 * MHz, | 
|  | .frequency_stepsize_hz	=  125 * kHz, | 
|  | .symbol_rate_min	= 1000000, | 
|  | .symbol_rate_max	= 45000000, | 
|  | .symbol_rate_tolerance	= 500,	/* ppm */ | 
|  | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | 
|  | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | | 
|  | FE_CAN_QPSK | | 
|  | FE_CAN_FEC_AUTO | 
|  | }, | 
|  |  | 
|  | .release = si21xx_release, | 
|  | .init = si21xx_init, | 
|  | .sleep = si21xx_sleep, | 
|  | .write = si21_write, | 
|  | .read_status = si21_read_status, | 
|  | .read_ber = si21_read_ber, | 
|  | .read_signal_strength = si21_read_signal_strength, | 
|  | .read_snr = si21_read_snr, | 
|  | .read_ucblocks = si21_read_ucblocks, | 
|  | .diseqc_send_master_cmd = si21xx_send_diseqc_msg, | 
|  | .diseqc_send_burst = si21xx_send_diseqc_burst, | 
|  | .set_tone = si21xx_set_tone, | 
|  | .set_voltage = si21xx_set_voltage, | 
|  |  | 
|  | .set_frontend = si21xx_set_frontend, | 
|  | }; | 
|  |  | 
|  | struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, | 
|  | struct i2c_adapter *i2c) | 
|  | { | 
|  | struct si21xx_state *state = NULL; | 
|  | int id; | 
|  |  | 
|  | dprintk("%s\n", __func__); | 
|  |  | 
|  | /* allocate memory for the internal state */ | 
|  | state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); | 
|  | if (state == NULL) | 
|  | goto error; | 
|  |  | 
|  | /* setup the state */ | 
|  | state->config = config; | 
|  | state->i2c = i2c; | 
|  | state->initialised = 0; | 
|  | state->errmode = STATUS_BER; | 
|  |  | 
|  | /* check if the demod is there */ | 
|  | id = si21_readreg(state, SYSTEM_MODE_REG); | 
|  | si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ | 
|  | msleep(200); | 
|  | id = si21_readreg(state, 0x00); | 
|  |  | 
|  | /* register 0x00 contains: | 
|  | 0x34 for SI2107 | 
|  | 0x24 for SI2108 | 
|  | 0x14 for SI2109 | 
|  | 0x04 for SI2110 | 
|  | */ | 
|  | if (id != 0x04 && id != 0x14) | 
|  | goto error; | 
|  |  | 
|  | /* create dvb_frontend */ | 
|  | memcpy(&state->frontend.ops, &si21xx_ops, | 
|  | sizeof(struct dvb_frontend_ops)); | 
|  | state->frontend.demodulator_priv = state; | 
|  | return &state->frontend; | 
|  |  | 
|  | error: | 
|  | kfree(state); | 
|  | return NULL; | 
|  | } | 
|  | EXPORT_SYMBOL(si21xx_attach); | 
|  |  | 
|  | module_param(debug, int, 0644); | 
|  | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | 
|  |  | 
|  | MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); | 
|  | MODULE_AUTHOR("Igor M. Liplianin"); | 
|  | MODULE_LICENSE("GPL"); |