xref: /openbmc/linux/drivers/media/dvb-frontends/si21xx.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
39a0bf528SMauro Carvalho Chehab *
49a0bf528SMauro Carvalho Chehab * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
59a0bf528SMauro Carvalho Chehab */
69a0bf528SMauro Carvalho Chehab #include <linux/init.h>
79a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
89a0bf528SMauro Carvalho Chehab #include <linux/module.h>
99a0bf528SMauro Carvalho Chehab #include <linux/string.h>
109a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
119a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
129a0bf528SMauro Carvalho Chehab #include <asm/div64.h>
139a0bf528SMauro Carvalho Chehab 
14fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
159a0bf528SMauro Carvalho Chehab #include "si21xx.h"
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab #define	REVISION_REG			0x00
189a0bf528SMauro Carvalho Chehab #define	SYSTEM_MODE_REG			0x01
199a0bf528SMauro Carvalho Chehab #define	TS_CTRL_REG_1			0x02
209a0bf528SMauro Carvalho Chehab #define	TS_CTRL_REG_2			0x03
219a0bf528SMauro Carvalho Chehab #define	PIN_CTRL_REG_1			0x04
229a0bf528SMauro Carvalho Chehab #define	PIN_CTRL_REG_2			0x05
239a0bf528SMauro Carvalho Chehab #define	LOCK_STATUS_REG_1		0x0f
249a0bf528SMauro Carvalho Chehab #define	LOCK_STATUS_REG_2		0x10
259a0bf528SMauro Carvalho Chehab #define	ACQ_STATUS_REG			0x11
269a0bf528SMauro Carvalho Chehab #define	ACQ_CTRL_REG_1			0x13
279a0bf528SMauro Carvalho Chehab #define	ACQ_CTRL_REG_2			0x14
289a0bf528SMauro Carvalho Chehab #define	PLL_DIVISOR_REG			0x15
299a0bf528SMauro Carvalho Chehab #define	COARSE_TUNE_REG			0x16
309a0bf528SMauro Carvalho Chehab #define	FINE_TUNE_REG_L			0x17
319a0bf528SMauro Carvalho Chehab #define	FINE_TUNE_REG_H			0x18
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab #define	ANALOG_AGC_POWER_LEVEL_REG	0x28
349a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_1	0x29
359a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_2	0x2a
369a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_3	0x2b
379a0bf528SMauro Carvalho Chehab 
389a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_L		0x31
399a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_M		0x32
409a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_H		0x33
419a0bf528SMauro Carvalho Chehab 
429a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_OFFSET_REG_L	0x36
439a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_OFFSET_REG_H	0x37
449a0bf528SMauro Carvalho Chehab #define	CFO_ERROR_REG_L			0x38
459a0bf528SMauro Carvalho Chehab #define	CFO_ERROR_REG_H			0x39
469a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_CTRL_REG	0x3a
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_L			0x3f
499a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_M			0x40
509a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_H			0x41
519a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_MAXIMUM_REG	0x42
529a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_MINIMUM_REG	0x43
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_CTRL_REG		0x7c
559a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_THRSHLD_REG	0x7d
569a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_LEVEL_REG_L	0x7e
579a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_LEVEL_REG_H	0x7f
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab #define	BLIND_SCAN_CTRL_REG		0x80
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab #define	LSA_CTRL_REG_1			0x8D
629a0bf528SMauro Carvalho Chehab #define	SPCTRM_TILT_CORR_THRSHLD_REG	0x8f
639a0bf528SMauro Carvalho Chehab #define	ONE_DB_BNDWDTH_THRSHLD_REG	0x90
649a0bf528SMauro Carvalho Chehab #define	TWO_DB_BNDWDTH_THRSHLD_REG	0x91
659a0bf528SMauro Carvalho Chehab #define	THREE_DB_BNDWDTH_THRSHLD_REG	0x92
669a0bf528SMauro Carvalho Chehab #define	INBAND_POWER_THRSHLD_REG	0x93
679a0bf528SMauro Carvalho Chehab #define	REF_NOISE_LVL_MRGN_THRSHLD_REG	0x94
689a0bf528SMauro Carvalho Chehab 
699a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_1		0xa0
709a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_2		0xa1
719a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_3		0xa2
729a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_STATUS_REG		0xa3
739a0bf528SMauro Carvalho Chehab #define	VITERBI_BER_COUNT_REG_L		0xab
749a0bf528SMauro Carvalho Chehab #define	REED_SOLOMON_CTRL_REG		0xb0
759a0bf528SMauro Carvalho Chehab #define	REED_SOLOMON_ERROR_COUNT_REG_L	0xb1
769a0bf528SMauro Carvalho Chehab #define	PRBS_CTRL_REG			0xb5
779a0bf528SMauro Carvalho Chehab 
789a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_1			0xc0
799a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_2			0xc1
809a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_3			0xc2
819a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_4			0xc3
829a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_STATUS_REG		0xc4
839a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_0			0xc5
849a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_1			0xc6
859a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_2			0xc7
869a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_3			0xc8
879a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_4			0xc9
889a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_5			0xca
899a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_1		0xcb
909a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_2		0xcc
919a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_3		0xcd
929a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_4		0xce
939a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_STATUS_REG		0xcf
949a0bf528SMauro Carvalho Chehab 
959a0bf528SMauro Carvalho Chehab #define FAIL	-1
969a0bf528SMauro Carvalho Chehab #define PASS	0
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab #define ALLOWABLE_FS_COUNT	10
999a0bf528SMauro Carvalho Chehab #define STATUS_BER		0
1009a0bf528SMauro Carvalho Chehab #define STATUS_UCBLOCKS		1
1019a0bf528SMauro Carvalho Chehab 
1029a0bf528SMauro Carvalho Chehab static int debug;
1039a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
1049a0bf528SMauro Carvalho Chehab 	do { \
1059a0bf528SMauro Carvalho Chehab 		if (debug) \
1069a0bf528SMauro Carvalho Chehab 			printk(KERN_DEBUG "si21xx: " args); \
1079a0bf528SMauro Carvalho Chehab 	} while (0)
1089a0bf528SMauro Carvalho Chehab 
1099a0bf528SMauro Carvalho Chehab enum {
1109a0bf528SMauro Carvalho Chehab 	ACTIVE_HIGH,
1119a0bf528SMauro Carvalho Chehab 	ACTIVE_LOW
1129a0bf528SMauro Carvalho Chehab };
1139a0bf528SMauro Carvalho Chehab enum {
1149a0bf528SMauro Carvalho Chehab 	BYTE_WIDE,
1159a0bf528SMauro Carvalho Chehab 	BIT_WIDE
1169a0bf528SMauro Carvalho Chehab };
1179a0bf528SMauro Carvalho Chehab enum {
1189a0bf528SMauro Carvalho Chehab 	CLK_GAPPED_MODE,
1199a0bf528SMauro Carvalho Chehab 	CLK_CONTINUOUS_MODE
1209a0bf528SMauro Carvalho Chehab };
1219a0bf528SMauro Carvalho Chehab enum {
1229a0bf528SMauro Carvalho Chehab 	RISING_EDGE,
1239a0bf528SMauro Carvalho Chehab 	FALLING_EDGE
1249a0bf528SMauro Carvalho Chehab };
1259a0bf528SMauro Carvalho Chehab enum {
1269a0bf528SMauro Carvalho Chehab 	MSB_FIRST,
1279a0bf528SMauro Carvalho Chehab 	LSB_FIRST
1289a0bf528SMauro Carvalho Chehab };
1299a0bf528SMauro Carvalho Chehab enum {
1309a0bf528SMauro Carvalho Chehab 	SERIAL,
1319a0bf528SMauro Carvalho Chehab 	PARALLEL
1329a0bf528SMauro Carvalho Chehab };
1339a0bf528SMauro Carvalho Chehab 
1349a0bf528SMauro Carvalho Chehab struct si21xx_state {
1359a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
1369a0bf528SMauro Carvalho Chehab 	const struct si21xx_config *config;
1379a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
1389a0bf528SMauro Carvalho Chehab 	u8 initialised:1;
1399a0bf528SMauro Carvalho Chehab 	int errmode;
1409a0bf528SMauro Carvalho Chehab 	int fs;			/*Sampling rate of the ADC in MHz*/
1419a0bf528SMauro Carvalho Chehab };
1429a0bf528SMauro Carvalho Chehab 
1439a0bf528SMauro Carvalho Chehab /*	register default initialization */
1449a0bf528SMauro Carvalho Chehab static u8 serit_sp1511lhb_inittab[] = {
1459a0bf528SMauro Carvalho Chehab 	0x01, 0x28,	/* set i2c_inc_disable */
1469a0bf528SMauro Carvalho Chehab 	0x20, 0x03,
1479a0bf528SMauro Carvalho Chehab 	0x27, 0x20,
1489a0bf528SMauro Carvalho Chehab 	0xe0, 0x45,
1499a0bf528SMauro Carvalho Chehab 	0xe1, 0x08,
1509a0bf528SMauro Carvalho Chehab 	0xfe, 0x01,
1519a0bf528SMauro Carvalho Chehab 	0x01, 0x28,
1529a0bf528SMauro Carvalho Chehab 	0x89, 0x09,
1539a0bf528SMauro Carvalho Chehab 	0x04, 0x80,
1549a0bf528SMauro Carvalho Chehab 	0x05, 0x01,
1559a0bf528SMauro Carvalho Chehab 	0x06, 0x00,
1569a0bf528SMauro Carvalho Chehab 	0x20, 0x03,
1579a0bf528SMauro Carvalho Chehab 	0x24, 0x88,
1589a0bf528SMauro Carvalho Chehab 	0x29, 0x09,
1599a0bf528SMauro Carvalho Chehab 	0x2a, 0x0f,
1609a0bf528SMauro Carvalho Chehab 	0x2c, 0x10,
1619a0bf528SMauro Carvalho Chehab 	0x2d, 0x19,
1629a0bf528SMauro Carvalho Chehab 	0x2e, 0x08,
1639a0bf528SMauro Carvalho Chehab 	0x2f, 0x10,
1649a0bf528SMauro Carvalho Chehab 	0x30, 0x19,
1659a0bf528SMauro Carvalho Chehab 	0x34, 0x20,
1669a0bf528SMauro Carvalho Chehab 	0x35, 0x03,
1679a0bf528SMauro Carvalho Chehab 	0x45, 0x02,
1689a0bf528SMauro Carvalho Chehab 	0x46, 0x45,
1699a0bf528SMauro Carvalho Chehab 	0x47, 0xd0,
1709a0bf528SMauro Carvalho Chehab 	0x48, 0x00,
1719a0bf528SMauro Carvalho Chehab 	0x49, 0x40,
1729a0bf528SMauro Carvalho Chehab 	0x4a, 0x03,
1739a0bf528SMauro Carvalho Chehab 	0x4c, 0xfd,
1749a0bf528SMauro Carvalho Chehab 	0x4f, 0x2e,
1759a0bf528SMauro Carvalho Chehab 	0x50, 0x2e,
1769a0bf528SMauro Carvalho Chehab 	0x51, 0x10,
1779a0bf528SMauro Carvalho Chehab 	0x52, 0x10,
1789a0bf528SMauro Carvalho Chehab 	0x56, 0x92,
1799a0bf528SMauro Carvalho Chehab 	0x59, 0x00,
1809a0bf528SMauro Carvalho Chehab 	0x5a, 0x2d,
1819a0bf528SMauro Carvalho Chehab 	0x5b, 0x33,
1829a0bf528SMauro Carvalho Chehab 	0x5c, 0x1f,
1839a0bf528SMauro Carvalho Chehab 	0x5f, 0x76,
1849a0bf528SMauro Carvalho Chehab 	0x62, 0xc0,
1859a0bf528SMauro Carvalho Chehab 	0x63, 0xc0,
1869a0bf528SMauro Carvalho Chehab 	0x64, 0xf3,
1879a0bf528SMauro Carvalho Chehab 	0x65, 0xf3,
1889a0bf528SMauro Carvalho Chehab 	0x79, 0x40,
1899a0bf528SMauro Carvalho Chehab 	0x6a, 0x40,
1909a0bf528SMauro Carvalho Chehab 	0x6b, 0x0a,
1919a0bf528SMauro Carvalho Chehab 	0x6c, 0x80,
1929a0bf528SMauro Carvalho Chehab 	0x6d, 0x27,
1939a0bf528SMauro Carvalho Chehab 	0x71, 0x06,
1949a0bf528SMauro Carvalho Chehab 	0x75, 0x60,
1959a0bf528SMauro Carvalho Chehab 	0x78, 0x00,
1969a0bf528SMauro Carvalho Chehab 	0x79, 0xb5,
1979a0bf528SMauro Carvalho Chehab 	0x7c, 0x05,
1989a0bf528SMauro Carvalho Chehab 	0x7d, 0x1a,
1999a0bf528SMauro Carvalho Chehab 	0x87, 0x55,
2009a0bf528SMauro Carvalho Chehab 	0x88, 0x72,
2019a0bf528SMauro Carvalho Chehab 	0x8f, 0x08,
2029a0bf528SMauro Carvalho Chehab 	0x90, 0xe0,
2039a0bf528SMauro Carvalho Chehab 	0x94, 0x40,
2049a0bf528SMauro Carvalho Chehab 	0xa0, 0x3f,
2059a0bf528SMauro Carvalho Chehab 	0xa1, 0xc0,
2069a0bf528SMauro Carvalho Chehab 	0xa4, 0xcc,
2079a0bf528SMauro Carvalho Chehab 	0xa5, 0x66,
2089a0bf528SMauro Carvalho Chehab 	0xa6, 0x66,
2099a0bf528SMauro Carvalho Chehab 	0xa7, 0x7b,
2109a0bf528SMauro Carvalho Chehab 	0xa8, 0x7b,
2119a0bf528SMauro Carvalho Chehab 	0xa9, 0x7b,
2129a0bf528SMauro Carvalho Chehab 	0xaa, 0x9a,
2139a0bf528SMauro Carvalho Chehab 	0xed, 0x04,
2149a0bf528SMauro Carvalho Chehab 	0xad, 0x00,
2159a0bf528SMauro Carvalho Chehab 	0xae, 0x03,
2169a0bf528SMauro Carvalho Chehab 	0xcc, 0xab,
2179a0bf528SMauro Carvalho Chehab 	0x01, 0x08,
2189a0bf528SMauro Carvalho Chehab 	0xff, 0xff
2199a0bf528SMauro Carvalho Chehab };
2209a0bf528SMauro Carvalho Chehab 
2219a0bf528SMauro Carvalho Chehab /*	low level read/writes */
si21_writeregs(struct si21xx_state * state,u8 reg1,u8 * data,int len)2229a0bf528SMauro Carvalho Chehab static int si21_writeregs(struct si21xx_state *state, u8 reg1,
2239a0bf528SMauro Carvalho Chehab 							u8 *data, int len)
2249a0bf528SMauro Carvalho Chehab {
2259a0bf528SMauro Carvalho Chehab 	int ret;
2269a0bf528SMauro Carvalho Chehab 	u8 buf[60];/* = { reg1, data };*/
2279a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
2289a0bf528SMauro Carvalho Chehab 				.addr = state->config->demod_address,
2299a0bf528SMauro Carvalho Chehab 				.flags = 0,
2309a0bf528SMauro Carvalho Chehab 				.buf = buf,
2319a0bf528SMauro Carvalho Chehab 				.len = len + 1
2329a0bf528SMauro Carvalho Chehab 	};
2339a0bf528SMauro Carvalho Chehab 
234cf3b576dSDan Carpenter 	if (len > sizeof(buf) - 1)
235cf3b576dSDan Carpenter 		return -EINVAL;
236cf3b576dSDan Carpenter 
2379a0bf528SMauro Carvalho Chehab 	msg.buf[0] =  reg1;
2389a0bf528SMauro Carvalho Chehab 	memcpy(msg.buf + 1, data, len);
2399a0bf528SMauro Carvalho Chehab 
2409a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
2419a0bf528SMauro Carvalho Chehab 
2429a0bf528SMauro Carvalho Chehab 	if (ret != 1)
2434bd69e7bSMauro Carvalho Chehab 		dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, ret == %i)\n",
2444bd69e7bSMauro Carvalho Chehab 			__func__, reg1, data[0], ret);
2459a0bf528SMauro Carvalho Chehab 
2469a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
2479a0bf528SMauro Carvalho Chehab }
2489a0bf528SMauro Carvalho Chehab 
si21_writereg(struct si21xx_state * state,u8 reg,u8 data)2499a0bf528SMauro Carvalho Chehab static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
2509a0bf528SMauro Carvalho Chehab {
2519a0bf528SMauro Carvalho Chehab 	int ret;
2529a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
2539a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
2549a0bf528SMauro Carvalho Chehab 				.addr = state->config->demod_address,
2559a0bf528SMauro Carvalho Chehab 				.flags = 0,
2569a0bf528SMauro Carvalho Chehab 				.buf = buf,
2579a0bf528SMauro Carvalho Chehab 				.len = 2
2589a0bf528SMauro Carvalho Chehab 	};
2599a0bf528SMauro Carvalho Chehab 
2609a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
2619a0bf528SMauro Carvalho Chehab 
2629a0bf528SMauro Carvalho Chehab 	if (ret != 1)
2634bd69e7bSMauro Carvalho Chehab 		dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, ret == %i)\n",
2644bd69e7bSMauro Carvalho Chehab 			__func__, reg, data, ret);
2659a0bf528SMauro Carvalho Chehab 
2669a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
2679a0bf528SMauro Carvalho Chehab }
2689a0bf528SMauro Carvalho Chehab 
si21_write(struct dvb_frontend * fe,const u8 buf[],int len)2699a0bf528SMauro Carvalho Chehab static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len)
2709a0bf528SMauro Carvalho Chehab {
2719a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
2729a0bf528SMauro Carvalho Chehab 
2739a0bf528SMauro Carvalho Chehab 	if (len != 2)
2749a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	return si21_writereg(state, buf[0], buf[1]);
2779a0bf528SMauro Carvalho Chehab }
2789a0bf528SMauro Carvalho Chehab 
si21_readreg(struct si21xx_state * state,u8 reg)2799a0bf528SMauro Carvalho Chehab static u8 si21_readreg(struct si21xx_state *state, u8 reg)
2809a0bf528SMauro Carvalho Chehab {
2819a0bf528SMauro Carvalho Chehab 	int ret;
2829a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
2839a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
2849a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
2859a0bf528SMauro Carvalho Chehab 		{
2869a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
2879a0bf528SMauro Carvalho Chehab 			.flags = 0,
2889a0bf528SMauro Carvalho Chehab 			.buf = b0,
2899a0bf528SMauro Carvalho Chehab 			.len = 1
2909a0bf528SMauro Carvalho Chehab 		}, {
2919a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
2929a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
2939a0bf528SMauro Carvalho Chehab 			.buf = b1,
2949a0bf528SMauro Carvalho Chehab 			.len = 1
2959a0bf528SMauro Carvalho Chehab 		}
2969a0bf528SMauro Carvalho Chehab 	};
2979a0bf528SMauro Carvalho Chehab 
2989a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
2999a0bf528SMauro Carvalho Chehab 
3009a0bf528SMauro Carvalho Chehab 	if (ret != 2)
3019a0bf528SMauro Carvalho Chehab 		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
3029a0bf528SMauro Carvalho Chehab 			__func__, reg, ret);
3039a0bf528SMauro Carvalho Chehab 
3049a0bf528SMauro Carvalho Chehab 	return b1[0];
3059a0bf528SMauro Carvalho Chehab }
3069a0bf528SMauro Carvalho Chehab 
si21_readregs(struct si21xx_state * state,u8 reg1,u8 * b,u8 len)3079a0bf528SMauro Carvalho Chehab static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
3089a0bf528SMauro Carvalho Chehab {
3099a0bf528SMauro Carvalho Chehab 	int ret;
3109a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
3119a0bf528SMauro Carvalho Chehab 		{
3129a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
3139a0bf528SMauro Carvalho Chehab 			.flags = 0,
3149a0bf528SMauro Carvalho Chehab 			.buf = &reg1,
3159a0bf528SMauro Carvalho Chehab 			.len = 1
3169a0bf528SMauro Carvalho Chehab 		}, {
3179a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
3189a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
3199a0bf528SMauro Carvalho Chehab 			.buf = b,
3209a0bf528SMauro Carvalho Chehab 			.len = len
3219a0bf528SMauro Carvalho Chehab 		}
3229a0bf528SMauro Carvalho Chehab 	};
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
3259a0bf528SMauro Carvalho Chehab 
3269a0bf528SMauro Carvalho Chehab 	if (ret != 2)
3279a0bf528SMauro Carvalho Chehab 		dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
3289a0bf528SMauro Carvalho Chehab 
3299a0bf528SMauro Carvalho Chehab 	return ret == 2 ? 0 : -1;
3309a0bf528SMauro Carvalho Chehab }
3319a0bf528SMauro Carvalho Chehab 
si21xx_wait_diseqc_idle(struct si21xx_state * state,int timeout)3329a0bf528SMauro Carvalho Chehab static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
3339a0bf528SMauro Carvalho Chehab {
3349a0bf528SMauro Carvalho Chehab 	unsigned long start = jiffies;
3359a0bf528SMauro Carvalho Chehab 
3369a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab 	while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) {
3391bcf233fSWang Qing 		if (time_is_before_jiffies(start + timeout)) {
3409a0bf528SMauro Carvalho Chehab 			dprintk("%s: timeout!!\n", __func__);
3419a0bf528SMauro Carvalho Chehab 			return -ETIMEDOUT;
3429a0bf528SMauro Carvalho Chehab 		}
3439a0bf528SMauro Carvalho Chehab 		msleep(10);
344c2c1b415SPeter Senna Tschudin 	}
3459a0bf528SMauro Carvalho Chehab 
3469a0bf528SMauro Carvalho Chehab 	return 0;
3479a0bf528SMauro Carvalho Chehab }
3489a0bf528SMauro Carvalho Chehab 
si21xx_set_symbolrate(struct dvb_frontend * fe,u32 srate)3499a0bf528SMauro Carvalho Chehab static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
3509a0bf528SMauro Carvalho Chehab {
3519a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
3529a0bf528SMauro Carvalho Chehab 	u32 sym_rate, data_rate;
3539a0bf528SMauro Carvalho Chehab 	int i;
3549a0bf528SMauro Carvalho Chehab 	u8 sym_rate_bytes[3];
3559a0bf528SMauro Carvalho Chehab 
3569a0bf528SMauro Carvalho Chehab 	dprintk("%s : srate = %i\n", __func__ , srate);
3579a0bf528SMauro Carvalho Chehab 
3589a0bf528SMauro Carvalho Chehab 	if ((srate < 1000000) || (srate > 45000000))
3599a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3609a0bf528SMauro Carvalho Chehab 
3619a0bf528SMauro Carvalho Chehab 	data_rate = srate;
3629a0bf528SMauro Carvalho Chehab 	sym_rate = 0;
3639a0bf528SMauro Carvalho Chehab 
3649a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 4; ++i) {
3659a0bf528SMauro Carvalho Chehab 		sym_rate /= 100;
3669a0bf528SMauro Carvalho Chehab 		sym_rate = sym_rate + ((data_rate % 100) * 0x800000) /
3679a0bf528SMauro Carvalho Chehab 								state->fs;
3689a0bf528SMauro Carvalho Chehab 		data_rate /= 100;
3699a0bf528SMauro Carvalho Chehab 	}
3709a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 3; ++i)
3719a0bf528SMauro Carvalho Chehab 		sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);
3729a0bf528SMauro Carvalho Chehab 
3739a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);
3749a0bf528SMauro Carvalho Chehab 
3759a0bf528SMauro Carvalho Chehab 	return 0;
3769a0bf528SMauro Carvalho Chehab }
3779a0bf528SMauro Carvalho Chehab 
si21xx_send_diseqc_msg(struct dvb_frontend * fe,struct dvb_diseqc_master_cmd * m)3789a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
3799a0bf528SMauro Carvalho Chehab 					struct dvb_diseqc_master_cmd *m)
3809a0bf528SMauro Carvalho Chehab {
3819a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
3829a0bf528SMauro Carvalho Chehab 	u8 lnb_status;
3839a0bf528SMauro Carvalho Chehab 	u8 LNB_CTRL_1;
3849a0bf528SMauro Carvalho Chehab 	int status;
3859a0bf528SMauro Carvalho Chehab 
3869a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
3879a0bf528SMauro Carvalho Chehab 
3889a0bf528SMauro Carvalho Chehab 	status = PASS;
3899a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 = 0;
3909a0bf528SMauro Carvalho Chehab 
3919a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01);
3929a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);
3939a0bf528SMauro Carvalho Chehab 
3949a0bf528SMauro Carvalho Chehab 	/*fill the FIFO*/
3959a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 = (lnb_status & 0x70);
3989a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 |= m->msg_len;
3999a0bf528SMauro Carvalho Chehab 
4009a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 |= 0x80;	/* begin LNB signaling */
4019a0bf528SMauro Carvalho Chehab 
4029a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);
4039a0bf528SMauro Carvalho Chehab 
4049a0bf528SMauro Carvalho Chehab 	return status;
4059a0bf528SMauro Carvalho Chehab }
4069a0bf528SMauro Carvalho Chehab 
si21xx_send_diseqc_burst(struct dvb_frontend * fe,enum fe_sec_mini_cmd burst)4079a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
4080df289a2SMauro Carvalho Chehab 				    enum fe_sec_mini_cmd burst)
4099a0bf528SMauro Carvalho Chehab {
4109a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
4119a0bf528SMauro Carvalho Chehab 	u8 val;
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
4149a0bf528SMauro Carvalho Chehab 
4159a0bf528SMauro Carvalho Chehab 	if (si21xx_wait_diseqc_idle(state, 100) < 0)
4169a0bf528SMauro Carvalho Chehab 		return -ETIMEDOUT;
4179a0bf528SMauro Carvalho Chehab 
4189a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, 0xc1));
4199a0bf528SMauro Carvalho Chehab 	if (si21_writereg(state, LNB_CTRL_REG_1,
4209a0bf528SMauro Carvalho Chehab 			burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
4219a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
4229a0bf528SMauro Carvalho Chehab 
4239a0bf528SMauro Carvalho Chehab 	if (si21xx_wait_diseqc_idle(state, 100) < 0)
4249a0bf528SMauro Carvalho Chehab 		return -ETIMEDOUT;
4259a0bf528SMauro Carvalho Chehab 
4269a0bf528SMauro Carvalho Chehab 	if (si21_writereg(state, LNB_CTRL_REG_1, val))
4279a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
4289a0bf528SMauro Carvalho Chehab 
4299a0bf528SMauro Carvalho Chehab 	return 0;
4309a0bf528SMauro Carvalho Chehab }
4319a0bf528SMauro Carvalho Chehab /*	30.06.2008 */
si21xx_set_tone(struct dvb_frontend * fe,enum fe_sec_tone_mode tone)4320df289a2SMauro Carvalho Chehab static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
4339a0bf528SMauro Carvalho Chehab {
4349a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
4359a0bf528SMauro Carvalho Chehab 	u8 val;
4369a0bf528SMauro Carvalho Chehab 
4379a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
4389a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab 	switch (tone) {
4419a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
4429a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);
4439a0bf528SMauro Carvalho Chehab 
4449a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
4459a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 	default:
4489a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4499a0bf528SMauro Carvalho Chehab 	}
4509a0bf528SMauro Carvalho Chehab }
4519a0bf528SMauro Carvalho Chehab 
si21xx_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage volt)4520df289a2SMauro Carvalho Chehab static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt)
4539a0bf528SMauro Carvalho Chehab {
4549a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
4559a0bf528SMauro Carvalho Chehab 
4569a0bf528SMauro Carvalho Chehab 	u8 val;
4579a0bf528SMauro Carvalho Chehab 	dprintk("%s: %s\n", __func__,
4589a0bf528SMauro Carvalho Chehab 		volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
4599a0bf528SMauro Carvalho Chehab 		volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
4609a0bf528SMauro Carvalho Chehab 
4619a0bf528SMauro Carvalho Chehab 
4629a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
4639a0bf528SMauro Carvalho Chehab 
4649a0bf528SMauro Carvalho Chehab 	switch (volt) {
4659a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
4669a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
4679a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
4689a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
4699a0bf528SMauro Carvalho Chehab 	default:
4709a0bf528SMauro Carvalho Chehab 		return -EINVAL;
471c2c1b415SPeter Senna Tschudin 	}
4729a0bf528SMauro Carvalho Chehab }
4739a0bf528SMauro Carvalho Chehab 
si21xx_init(struct dvb_frontend * fe)4749a0bf528SMauro Carvalho Chehab static int si21xx_init(struct dvb_frontend *fe)
4759a0bf528SMauro Carvalho Chehab {
4769a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
4779a0bf528SMauro Carvalho Chehab 	int i;
4789a0bf528SMauro Carvalho Chehab 	int status = 0;
4799a0bf528SMauro Carvalho Chehab 	u8 reg1;
4809a0bf528SMauro Carvalho Chehab 	u8 val;
4819a0bf528SMauro Carvalho Chehab 	u8 reg2[2];
4829a0bf528SMauro Carvalho Chehab 
4839a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 	for (i = 0; ; i += 2) {
4869a0bf528SMauro Carvalho Chehab 		reg1 = serit_sp1511lhb_inittab[i];
4879a0bf528SMauro Carvalho Chehab 		val = serit_sp1511lhb_inittab[i+1];
4889a0bf528SMauro Carvalho Chehab 		if (reg1 == 0xff && val == 0xff)
4899a0bf528SMauro Carvalho Chehab 			break;
4909a0bf528SMauro Carvalho Chehab 		si21_writeregs(state, reg1, &val, 1);
4919a0bf528SMauro Carvalho Chehab 	}
4929a0bf528SMauro Carvalho Chehab 
4939a0bf528SMauro Carvalho Chehab 	/*DVB QPSK SYSTEM MODE REG*/
4949a0bf528SMauro Carvalho Chehab 	reg1 = 0x08;
4959a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYSTEM_MODE_REG, &reg1, 0x01);
4969a0bf528SMauro Carvalho Chehab 
4979a0bf528SMauro Carvalho Chehab 	/*transport stream config*/
4989a0bf528SMauro Carvalho Chehab 	/*
4999a0bf528SMauro Carvalho Chehab 	mode = PARALLEL;
5009a0bf528SMauro Carvalho Chehab 	sdata_form = LSB_FIRST;
5019a0bf528SMauro Carvalho Chehab 	clk_edge = FALLING_EDGE;
5029a0bf528SMauro Carvalho Chehab 	clk_mode = CLK_GAPPED_MODE;
5039a0bf528SMauro Carvalho Chehab 	strt_len = BYTE_WIDE;
5049a0bf528SMauro Carvalho Chehab 	sync_pol = ACTIVE_HIGH;
5059a0bf528SMauro Carvalho Chehab 	val_pol = ACTIVE_HIGH;
5069a0bf528SMauro Carvalho Chehab 	err_pol = ACTIVE_HIGH;
5079a0bf528SMauro Carvalho Chehab 	sclk_rate = 0x00;
5089a0bf528SMauro Carvalho Chehab 	parity = 0x00 ;
5099a0bf528SMauro Carvalho Chehab 	data_delay = 0x00;
5109a0bf528SMauro Carvalho Chehab 	clk_delay = 0x00;
5119a0bf528SMauro Carvalho Chehab 	pclk_smooth = 0x00;
5129a0bf528SMauro Carvalho Chehab 	*/
5139a0bf528SMauro Carvalho Chehab 	reg2[0] =
5149a0bf528SMauro Carvalho Chehab 		PARALLEL + (LSB_FIRST << 1)
5159a0bf528SMauro Carvalho Chehab 		+ (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3)
5169a0bf528SMauro Carvalho Chehab 		+ (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5)
5179a0bf528SMauro Carvalho Chehab 		+ (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7);
5189a0bf528SMauro Carvalho Chehab 
5199a0bf528SMauro Carvalho Chehab 	reg2[1] = 0;
5209a0bf528SMauro Carvalho Chehab 	/*	sclk_rate + (parity << 2)
5219a0bf528SMauro Carvalho Chehab 		+ (data_delay << 3) + (clk_delay << 4)
5229a0bf528SMauro Carvalho Chehab 		+ (pclk_smooth << 5);
5239a0bf528SMauro Carvalho Chehab 	*/
5249a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02);
5259a0bf528SMauro Carvalho Chehab 	if (status != 0)
5269a0bf528SMauro Carvalho Chehab 		dprintk(" %s : TS Set Error\n", __func__);
5279a0bf528SMauro Carvalho Chehab 
5289a0bf528SMauro Carvalho Chehab 	return 0;
5299a0bf528SMauro Carvalho Chehab 
5309a0bf528SMauro Carvalho Chehab }
5319a0bf528SMauro Carvalho Chehab 
si21_read_status(struct dvb_frontend * fe,enum fe_status * status)5320df289a2SMauro Carvalho Chehab static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status)
5339a0bf528SMauro Carvalho Chehab {
5349a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
5359a0bf528SMauro Carvalho Chehab 	u8 regs_read[2];
5369a0bf528SMauro Carvalho Chehab 	u8 reg_read;
5379a0bf528SMauro Carvalho Chehab 	u8 i;
5389a0bf528SMauro Carvalho Chehab 	u8 lock;
5399a0bf528SMauro Carvalho Chehab 	u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
5409a0bf528SMauro Carvalho Chehab 
5419a0bf528SMauro Carvalho Chehab 	si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02);
5429a0bf528SMauro Carvalho Chehab 	reg_read = 0;
5439a0bf528SMauro Carvalho Chehab 
5449a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 7; ++i)
5459a0bf528SMauro Carvalho Chehab 		reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);
5469a0bf528SMauro Carvalho Chehab 
5479a0bf528SMauro Carvalho Chehab 	lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));
5489a0bf528SMauro Carvalho Chehab 
5499a0bf528SMauro Carvalho Chehab 	dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
5509a0bf528SMauro Carvalho Chehab 	*status = 0;
5519a0bf528SMauro Carvalho Chehab 
5529a0bf528SMauro Carvalho Chehab 	if (signal > 10)
5539a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
5549a0bf528SMauro Carvalho Chehab 
5559a0bf528SMauro Carvalho Chehab 	if (lock & 0x2)
5569a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
5579a0bf528SMauro Carvalho Chehab 
5589a0bf528SMauro Carvalho Chehab 	if (lock & 0x20)
5599a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
5609a0bf528SMauro Carvalho Chehab 
5619a0bf528SMauro Carvalho Chehab 	if (lock & 0x40)
5629a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
5639a0bf528SMauro Carvalho Chehab 
5649a0bf528SMauro Carvalho Chehab 	if ((lock & 0x7b) == 0x7b)
5659a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
5669a0bf528SMauro Carvalho Chehab 
5679a0bf528SMauro Carvalho Chehab 	return 0;
5689a0bf528SMauro Carvalho Chehab }
5699a0bf528SMauro Carvalho Chehab 
si21_read_signal_strength(struct dvb_frontend * fe,u16 * strength)5709a0bf528SMauro Carvalho Chehab static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
5719a0bf528SMauro Carvalho Chehab {
5729a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
5739a0bf528SMauro Carvalho Chehab 
5749a0bf528SMauro Carvalho Chehab 	/*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
5759a0bf528SMauro Carvalho Chehab 						(u8*)agclevel, 0x01);*/
5769a0bf528SMauro Carvalho Chehab 
5779a0bf528SMauro Carvalho Chehab 	u16 signal = (3 * si21_readreg(state, 0x27) *
5789a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x28));
5799a0bf528SMauro Carvalho Chehab 
5809a0bf528SMauro Carvalho Chehab 	dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
5819a0bf528SMauro Carvalho Chehab 		si21_readreg(state, 0x27),
5829a0bf528SMauro Carvalho Chehab 		si21_readreg(state, 0x28), (int) signal);
5839a0bf528SMauro Carvalho Chehab 
5849a0bf528SMauro Carvalho Chehab 	signal  <<= 4;
5859a0bf528SMauro Carvalho Chehab 	*strength = signal;
5869a0bf528SMauro Carvalho Chehab 
5879a0bf528SMauro Carvalho Chehab 	return 0;
5889a0bf528SMauro Carvalho Chehab }
5899a0bf528SMauro Carvalho Chehab 
si21_read_ber(struct dvb_frontend * fe,u32 * ber)5909a0bf528SMauro Carvalho Chehab static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
5919a0bf528SMauro Carvalho Chehab {
5929a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
5959a0bf528SMauro Carvalho Chehab 
5969a0bf528SMauro Carvalho Chehab 	if (state->errmode != STATUS_BER)
5979a0bf528SMauro Carvalho Chehab 		return 0;
5989a0bf528SMauro Carvalho Chehab 
5999a0bf528SMauro Carvalho Chehab 	*ber = (si21_readreg(state, 0x1d) << 8) |
6009a0bf528SMauro Carvalho Chehab 				si21_readreg(state, 0x1e);
6019a0bf528SMauro Carvalho Chehab 
6029a0bf528SMauro Carvalho Chehab 	return 0;
6039a0bf528SMauro Carvalho Chehab }
6049a0bf528SMauro Carvalho Chehab 
si21_read_snr(struct dvb_frontend * fe,u16 * snr)6059a0bf528SMauro Carvalho Chehab static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
6069a0bf528SMauro Carvalho Chehab {
6079a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
6089a0bf528SMauro Carvalho Chehab 
6099a0bf528SMauro Carvalho Chehab 	s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
6109a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x25));
6119a0bf528SMauro Carvalho Chehab 	xsnr = 3 * (xsnr - 0xa100);
6129a0bf528SMauro Carvalho Chehab 	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
6139a0bf528SMauro Carvalho Chehab 
6149a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
6159a0bf528SMauro Carvalho Chehab 
6169a0bf528SMauro Carvalho Chehab 	return 0;
6179a0bf528SMauro Carvalho Chehab }
6189a0bf528SMauro Carvalho Chehab 
si21_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)6199a0bf528SMauro Carvalho Chehab static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
6209a0bf528SMauro Carvalho Chehab {
6219a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
6229a0bf528SMauro Carvalho Chehab 
6239a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
6249a0bf528SMauro Carvalho Chehab 
6259a0bf528SMauro Carvalho Chehab 	if (state->errmode != STATUS_UCBLOCKS)
6269a0bf528SMauro Carvalho Chehab 		*ucblocks = 0;
6279a0bf528SMauro Carvalho Chehab 	else
6289a0bf528SMauro Carvalho Chehab 		*ucblocks = (si21_readreg(state, 0x1d) << 8) |
6299a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x1e);
6309a0bf528SMauro Carvalho Chehab 
6319a0bf528SMauro Carvalho Chehab 	return 0;
6329a0bf528SMauro Carvalho Chehab }
6339a0bf528SMauro Carvalho Chehab 
6349a0bf528SMauro Carvalho Chehab /*	initiates a channel acquisition sequence
6359a0bf528SMauro Carvalho Chehab 	using the specified symbol rate and code rate */
si21xx_setacquire(struct dvb_frontend * fe,int symbrate,enum fe_code_rate crate)6369a0bf528SMauro Carvalho Chehab static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
6370df289a2SMauro Carvalho Chehab 			     enum fe_code_rate crate)
6389a0bf528SMauro Carvalho Chehab {
6399a0bf528SMauro Carvalho Chehab 
6409a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
6419a0bf528SMauro Carvalho Chehab 	u8 coderates[] = {
6429a0bf528SMauro Carvalho Chehab 				0x0, 0x01, 0x02, 0x04, 0x00,
6439a0bf528SMauro Carvalho Chehab 				0x8, 0x10, 0x20, 0x00, 0x3f
6449a0bf528SMauro Carvalho Chehab 	};
6459a0bf528SMauro Carvalho Chehab 
6469a0bf528SMauro Carvalho Chehab 	u8 coderate_ptr;
6479a0bf528SMauro Carvalho Chehab 	int status;
6489a0bf528SMauro Carvalho Chehab 	u8 start_acq = 0x80;
6499a0bf528SMauro Carvalho Chehab 	u8 reg, regs[3];
6509a0bf528SMauro Carvalho Chehab 
6519a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
6529a0bf528SMauro Carvalho Chehab 
6539a0bf528SMauro Carvalho Chehab 	status = PASS;
6549a0bf528SMauro Carvalho Chehab 	coderate_ptr = coderates[crate];
6559a0bf528SMauro Carvalho Chehab 
6569a0bf528SMauro Carvalho Chehab 	si21xx_set_symbolrate(fe, symbrate);
6579a0bf528SMauro Carvalho Chehab 
6589a0bf528SMauro Carvalho Chehab 	/* write code rates to use in the Viterbi search */
6599a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
6609a0bf528SMauro Carvalho Chehab 				VIT_SRCH_CTRL_REG_1,
6619a0bf528SMauro Carvalho Chehab 				&coderate_ptr, 0x01);
6629a0bf528SMauro Carvalho Chehab 
6639a0bf528SMauro Carvalho Chehab 	/* clear acq_start bit */
6649a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
6659a0bf528SMauro Carvalho Chehab 	reg &= ~start_acq;
6669a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
6679a0bf528SMauro Carvalho Chehab 
6689a0bf528SMauro Carvalho Chehab 	/* use new Carrier Frequency Offset Estimator (QuickLock) */
6699a0bf528SMauro Carvalho Chehab 	regs[0] = 0xCB;
6709a0bf528SMauro Carvalho Chehab 	regs[1] = 0x40;
6719a0bf528SMauro Carvalho Chehab 	regs[2] = 0xCB;
6729a0bf528SMauro Carvalho Chehab 
6739a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
6749a0bf528SMauro Carvalho Chehab 				TWO_DB_BNDWDTH_THRSHLD_REG,
6759a0bf528SMauro Carvalho Chehab 				&regs[0], 0x03);
6769a0bf528SMauro Carvalho Chehab 	reg = 0x56;
6779a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
6789a0bf528SMauro Carvalho Chehab 				LSA_CTRL_REG_1, &reg, 1);
6799a0bf528SMauro Carvalho Chehab 	reg = 0x05;
6809a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
6819a0bf528SMauro Carvalho Chehab 				BLIND_SCAN_CTRL_REG, &reg, 1);
6829a0bf528SMauro Carvalho Chehab 	/* start automatic acq */
6839a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
6849a0bf528SMauro Carvalho Chehab 				ACQ_CTRL_REG_2, &start_acq, 0x01);
6859a0bf528SMauro Carvalho Chehab 
6869a0bf528SMauro Carvalho Chehab 	return status;
6879a0bf528SMauro Carvalho Chehab }
6889a0bf528SMauro Carvalho Chehab 
si21xx_set_frontend(struct dvb_frontend * fe)6899a0bf528SMauro Carvalho Chehab static int si21xx_set_frontend(struct dvb_frontend *fe)
6909a0bf528SMauro Carvalho Chehab {
6919a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
6929a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
6939a0bf528SMauro Carvalho Chehab 
6949a0bf528SMauro Carvalho Chehab 	/* freq		Channel carrier frequency in KHz (i.e. 1550000 KHz)
6959a0bf528SMauro Carvalho Chehab 	 datarate	Channel symbol rate in Sps (i.e. 22500000 Sps)*/
6969a0bf528SMauro Carvalho Chehab 
6979a0bf528SMauro Carvalho Chehab 	/* in MHz */
6989a0bf528SMauro Carvalho Chehab 	unsigned char coarse_tune_freq;
6999a0bf528SMauro Carvalho Chehab 	int fine_tune_freq;
7009a0bf528SMauro Carvalho Chehab 	unsigned char sample_rate = 0;
7019a0bf528SMauro Carvalho Chehab 	/* boolean */
7029a0bf528SMauro Carvalho Chehab 	bool inband_interferer_ind;
7039a0bf528SMauro Carvalho Chehab 
7049a0bf528SMauro Carvalho Chehab 	/* INTERMEDIATE VALUES */
7059a0bf528SMauro Carvalho Chehab 	int icoarse_tune_freq; /* MHz */
7069a0bf528SMauro Carvalho Chehab 	int ifine_tune_freq; /* MHz */
7079a0bf528SMauro Carvalho Chehab 	unsigned int band_high;
7089a0bf528SMauro Carvalho Chehab 	unsigned int band_low;
7099a0bf528SMauro Carvalho Chehab 	unsigned int x1;
7109a0bf528SMauro Carvalho Chehab 	unsigned int x2;
7119a0bf528SMauro Carvalho Chehab 	int i;
7129a0bf528SMauro Carvalho Chehab 	bool inband_interferer_div2[ALLOWABLE_FS_COUNT];
7139a0bf528SMauro Carvalho Chehab 	bool inband_interferer_div4[ALLOWABLE_FS_COUNT];
71468cfde02SMauro Carvalho Chehab 	int status = 0;
7159a0bf528SMauro Carvalho Chehab 
7169a0bf528SMauro Carvalho Chehab 	/* allowable sample rates for ADC in MHz */
7179a0bf528SMauro Carvalho Chehab 	int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195,
7189a0bf528SMauro Carvalho Chehab 					196, 204, 205, 206, 207
7199a0bf528SMauro Carvalho Chehab 	};
7209a0bf528SMauro Carvalho Chehab 	/* in MHz */
7219a0bf528SMauro Carvalho Chehab 	int if_limit_high;
7229a0bf528SMauro Carvalho Chehab 	int if_limit_low;
7239a0bf528SMauro Carvalho Chehab 	int lnb_lo;
7249a0bf528SMauro Carvalho Chehab 	int lnb_uncertanity;
7259a0bf528SMauro Carvalho Chehab 
7269a0bf528SMauro Carvalho Chehab 	int rf_freq;
7279a0bf528SMauro Carvalho Chehab 	int data_rate;
7289a0bf528SMauro Carvalho Chehab 	unsigned char regs[4];
7299a0bf528SMauro Carvalho Chehab 
7309a0bf528SMauro Carvalho Chehab 	dprintk("%s : FE_SET_FRONTEND\n", __func__);
7319a0bf528SMauro Carvalho Chehab 
7329a0bf528SMauro Carvalho Chehab 	if (c->delivery_system != SYS_DVBS) {
7339a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported delivery system selected (%d)\n",
7349a0bf528SMauro Carvalho Chehab 				__func__, c->delivery_system);
7359a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
7369a0bf528SMauro Carvalho Chehab 	}
7379a0bf528SMauro Carvalho Chehab 
7389a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
7399a0bf528SMauro Carvalho Chehab 		inband_interferer_div2[i] = inband_interferer_div4[i] = false;
7409a0bf528SMauro Carvalho Chehab 
7419a0bf528SMauro Carvalho Chehab 	if_limit_high = -700000;
7429a0bf528SMauro Carvalho Chehab 	if_limit_low = -100000;
7439a0bf528SMauro Carvalho Chehab 	/* in MHz */
7449a0bf528SMauro Carvalho Chehab 	lnb_lo = 0;
7459a0bf528SMauro Carvalho Chehab 	lnb_uncertanity = 0;
7469a0bf528SMauro Carvalho Chehab 
7479a0bf528SMauro Carvalho Chehab 	rf_freq = 10 * c->frequency ;
7489a0bf528SMauro Carvalho Chehab 	data_rate = c->symbol_rate / 100;
7499a0bf528SMauro Carvalho Chehab 
7509a0bf528SMauro Carvalho Chehab 	band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200)
7519a0bf528SMauro Carvalho Chehab 					+ (data_rate * 135)) / 200;
7529a0bf528SMauro Carvalho Chehab 
7539a0bf528SMauro Carvalho Chehab 	band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200)
7549a0bf528SMauro Carvalho Chehab 					+ (data_rate * 135)) / 200;
7559a0bf528SMauro Carvalho Chehab 
7569a0bf528SMauro Carvalho Chehab 
7579a0bf528SMauro Carvalho Chehab 	icoarse_tune_freq = 100000 *
7589a0bf528SMauro Carvalho Chehab 				(((rf_freq - lnb_lo) -
7599a0bf528SMauro Carvalho Chehab 					(if_limit_low + if_limit_high) / 2)
7609a0bf528SMauro Carvalho Chehab 								/ 100000);
7619a0bf528SMauro Carvalho Chehab 
7629a0bf528SMauro Carvalho Chehab 	ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
7639a0bf528SMauro Carvalho Chehab 
7649a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
7659a0bf528SMauro Carvalho Chehab 		x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
7669a0bf528SMauro Carvalho Chehab 					(afs[i] * 2500) + afs[i] * 2500;
7679a0bf528SMauro Carvalho Chehab 
7689a0bf528SMauro Carvalho Chehab 		x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
7699a0bf528SMauro Carvalho Chehab 							(afs[i] * 2500);
7709a0bf528SMauro Carvalho Chehab 
7719a0bf528SMauro Carvalho Chehab 		if (((band_low < x1) && (x1 < band_high)) ||
7729a0bf528SMauro Carvalho Chehab 					((band_low < x2) && (x2 < band_high)))
7739a0bf528SMauro Carvalho Chehab 					inband_interferer_div4[i] = true;
7749a0bf528SMauro Carvalho Chehab 
7759a0bf528SMauro Carvalho Chehab 	}
7769a0bf528SMauro Carvalho Chehab 
7779a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
7789a0bf528SMauro Carvalho Chehab 		x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
7799a0bf528SMauro Carvalho Chehab 					(afs[i] * 5000) + afs[i] * 5000;
7809a0bf528SMauro Carvalho Chehab 
7819a0bf528SMauro Carvalho Chehab 		x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
7829a0bf528SMauro Carvalho Chehab 					(afs[i] * 5000);
7839a0bf528SMauro Carvalho Chehab 
7849a0bf528SMauro Carvalho Chehab 		if (((band_low < x1) && (x1 < band_high)) ||
7859a0bf528SMauro Carvalho Chehab 					((band_low < x2) && (x2 < band_high)))
7869a0bf528SMauro Carvalho Chehab 					inband_interferer_div2[i] = true;
7879a0bf528SMauro Carvalho Chehab 	}
7889a0bf528SMauro Carvalho Chehab 
7899a0bf528SMauro Carvalho Chehab 	inband_interferer_ind = true;
7909a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
7919a0bf528SMauro Carvalho Chehab 		if (inband_interferer_div2[i] || inband_interferer_div4[i]) {
7929a0bf528SMauro Carvalho Chehab 			inband_interferer_ind = false;
7939a0bf528SMauro Carvalho Chehab 			break;
7949a0bf528SMauro Carvalho Chehab 		}
7959a0bf528SMauro Carvalho Chehab 	}
7969a0bf528SMauro Carvalho Chehab 
7979a0bf528SMauro Carvalho Chehab 	if (inband_interferer_ind) {
7989a0bf528SMauro Carvalho Chehab 		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
7999a0bf528SMauro Carvalho Chehab 			if (!inband_interferer_div2[i]) {
8009a0bf528SMauro Carvalho Chehab 				sample_rate = (u8) afs[i];
8019a0bf528SMauro Carvalho Chehab 				break;
8029a0bf528SMauro Carvalho Chehab 			}
8039a0bf528SMauro Carvalho Chehab 		}
8049a0bf528SMauro Carvalho Chehab 	} else {
8059a0bf528SMauro Carvalho Chehab 		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
8069a0bf528SMauro Carvalho Chehab 			if ((inband_interferer_div2[i] ||
8079a0bf528SMauro Carvalho Chehab 			     !inband_interferer_div4[i])) {
8089a0bf528SMauro Carvalho Chehab 				sample_rate = (u8) afs[i];
8099a0bf528SMauro Carvalho Chehab 				break;
8109a0bf528SMauro Carvalho Chehab 			}
8119a0bf528SMauro Carvalho Chehab 		}
8129a0bf528SMauro Carvalho Chehab 
8139a0bf528SMauro Carvalho Chehab 	}
8149a0bf528SMauro Carvalho Chehab 
8159a0bf528SMauro Carvalho Chehab 	if (sample_rate > 207 || sample_rate < 192)
8169a0bf528SMauro Carvalho Chehab 		sample_rate = 200;
8179a0bf528SMauro Carvalho Chehab 
8189a0bf528SMauro Carvalho Chehab 	fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) /
8199a0bf528SMauro Carvalho Chehab 					((sample_rate) * 1000));
8209a0bf528SMauro Carvalho Chehab 
8219a0bf528SMauro Carvalho Chehab 	coarse_tune_freq = (u8)(icoarse_tune_freq / 100000);
8229a0bf528SMauro Carvalho Chehab 
8239a0bf528SMauro Carvalho Chehab 	regs[0] = sample_rate;
8249a0bf528SMauro Carvalho Chehab 	regs[1] = coarse_tune_freq;
8259a0bf528SMauro Carvalho Chehab 	regs[2] = fine_tune_freq & 0xFF;
8269a0bf528SMauro Carvalho Chehab 	regs[3] = fine_tune_freq >> 8 & 0xFF;
8279a0bf528SMauro Carvalho Chehab 
8289a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, PLL_DIVISOR_REG, &regs[0], 0x04);
8299a0bf528SMauro Carvalho Chehab 
8309a0bf528SMauro Carvalho Chehab 	state->fs = sample_rate;/*ADC MHz*/
8319a0bf528SMauro Carvalho Chehab 	si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
8329a0bf528SMauro Carvalho Chehab 
83368cfde02SMauro Carvalho Chehab 	if (status)
83468cfde02SMauro Carvalho Chehab 		return -EREMOTEIO;
83568cfde02SMauro Carvalho Chehab 
8369a0bf528SMauro Carvalho Chehab 	return 0;
8379a0bf528SMauro Carvalho Chehab }
8389a0bf528SMauro Carvalho Chehab 
si21xx_sleep(struct dvb_frontend * fe)8399a0bf528SMauro Carvalho Chehab static int si21xx_sleep(struct dvb_frontend *fe)
8409a0bf528SMauro Carvalho Chehab {
8419a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
8429a0bf528SMauro Carvalho Chehab 	u8 regdata;
8439a0bf528SMauro Carvalho Chehab 
8449a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
8459a0bf528SMauro Carvalho Chehab 
8469a0bf528SMauro Carvalho Chehab 	si21_readregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
8479a0bf528SMauro Carvalho Chehab 	regdata |= 1 << 6;
8489a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
8499a0bf528SMauro Carvalho Chehab 	state->initialised = 0;
8509a0bf528SMauro Carvalho Chehab 
8519a0bf528SMauro Carvalho Chehab 	return 0;
8529a0bf528SMauro Carvalho Chehab }
8539a0bf528SMauro Carvalho Chehab 
si21xx_release(struct dvb_frontend * fe)8549a0bf528SMauro Carvalho Chehab static void si21xx_release(struct dvb_frontend *fe)
8559a0bf528SMauro Carvalho Chehab {
8569a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
8579a0bf528SMauro Carvalho Chehab 
8589a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
8599a0bf528SMauro Carvalho Chehab 
8609a0bf528SMauro Carvalho Chehab 	kfree(state);
8619a0bf528SMauro Carvalho Chehab }
8629a0bf528SMauro Carvalho Chehab 
863bd336e63SMax Kellermann static const struct dvb_frontend_ops si21xx_ops = {
8649a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
8659a0bf528SMauro Carvalho Chehab 	.info = {
8669a0bf528SMauro Carvalho Chehab 		.name			= "SL SI21XX DVB-S",
867f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  950 * MHz,
868f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 2150 * MHz,
869f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	=  125 * kHz,
8709a0bf528SMauro Carvalho Chehab 		.symbol_rate_min	= 1000000,
8719a0bf528SMauro Carvalho Chehab 		.symbol_rate_max	= 45000000,
8729a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance	= 500,	/* ppm */
8739a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
8749a0bf528SMauro Carvalho Chehab 		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
8759a0bf528SMauro Carvalho Chehab 		FE_CAN_QPSK |
8769a0bf528SMauro Carvalho Chehab 		FE_CAN_FEC_AUTO
8779a0bf528SMauro Carvalho Chehab 	},
8789a0bf528SMauro Carvalho Chehab 
8799a0bf528SMauro Carvalho Chehab 	.release = si21xx_release,
8809a0bf528SMauro Carvalho Chehab 	.init = si21xx_init,
8819a0bf528SMauro Carvalho Chehab 	.sleep = si21xx_sleep,
8829a0bf528SMauro Carvalho Chehab 	.write = si21_write,
8839a0bf528SMauro Carvalho Chehab 	.read_status = si21_read_status,
8849a0bf528SMauro Carvalho Chehab 	.read_ber = si21_read_ber,
8859a0bf528SMauro Carvalho Chehab 	.read_signal_strength = si21_read_signal_strength,
8869a0bf528SMauro Carvalho Chehab 	.read_snr = si21_read_snr,
8879a0bf528SMauro Carvalho Chehab 	.read_ucblocks = si21_read_ucblocks,
8889a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = si21xx_send_diseqc_msg,
8899a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = si21xx_send_diseqc_burst,
8909a0bf528SMauro Carvalho Chehab 	.set_tone = si21xx_set_tone,
8919a0bf528SMauro Carvalho Chehab 	.set_voltage = si21xx_set_voltage,
8929a0bf528SMauro Carvalho Chehab 
8939a0bf528SMauro Carvalho Chehab 	.set_frontend = si21xx_set_frontend,
8949a0bf528SMauro Carvalho Chehab };
8959a0bf528SMauro Carvalho Chehab 
si21xx_attach(const struct si21xx_config * config,struct i2c_adapter * i2c)8969a0bf528SMauro Carvalho Chehab struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
8979a0bf528SMauro Carvalho Chehab 						struct i2c_adapter *i2c)
8989a0bf528SMauro Carvalho Chehab {
8999a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = NULL;
9009a0bf528SMauro Carvalho Chehab 	int id;
9019a0bf528SMauro Carvalho Chehab 
9029a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
9039a0bf528SMauro Carvalho Chehab 
9049a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
9059a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL);
9069a0bf528SMauro Carvalho Chehab 	if (state == NULL)
9079a0bf528SMauro Carvalho Chehab 		goto error;
9089a0bf528SMauro Carvalho Chehab 
9099a0bf528SMauro Carvalho Chehab 	/* setup the state */
9109a0bf528SMauro Carvalho Chehab 	state->config = config;
9119a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
9129a0bf528SMauro Carvalho Chehab 	state->initialised = 0;
9139a0bf528SMauro Carvalho Chehab 	state->errmode = STATUS_BER;
9149a0bf528SMauro Carvalho Chehab 
9159a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
9169a0bf528SMauro Carvalho Chehab 	id = si21_readreg(state, SYSTEM_MODE_REG);
9179a0bf528SMauro Carvalho Chehab 	si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */
9189a0bf528SMauro Carvalho Chehab 	msleep(200);
9199a0bf528SMauro Carvalho Chehab 	id = si21_readreg(state, 0x00);
9209a0bf528SMauro Carvalho Chehab 
9219a0bf528SMauro Carvalho Chehab 	/* register 0x00 contains:
9229a0bf528SMauro Carvalho Chehab 		0x34 for SI2107
9239a0bf528SMauro Carvalho Chehab 		0x24 for SI2108
9249a0bf528SMauro Carvalho Chehab 		0x14 for SI2109
9259a0bf528SMauro Carvalho Chehab 		0x04 for SI2110
9269a0bf528SMauro Carvalho Chehab 	*/
9279a0bf528SMauro Carvalho Chehab 	if (id != 0x04 && id != 0x14)
9289a0bf528SMauro Carvalho Chehab 		goto error;
9299a0bf528SMauro Carvalho Chehab 
9309a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
9319a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &si21xx_ops,
9329a0bf528SMauro Carvalho Chehab 					sizeof(struct dvb_frontend_ops));
9339a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
9349a0bf528SMauro Carvalho Chehab 	return &state->frontend;
9359a0bf528SMauro Carvalho Chehab 
9369a0bf528SMauro Carvalho Chehab error:
9379a0bf528SMauro Carvalho Chehab 	kfree(state);
9389a0bf528SMauro Carvalho Chehab 	return NULL;
9399a0bf528SMauro Carvalho Chehab }
940*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(si21xx_attach);
9419a0bf528SMauro Carvalho Chehab 
9429a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
9439a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
9449a0bf528SMauro Carvalho Chehab 
9459a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver");
9469a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Igor M. Liplianin");
9479a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
948