xref: /openbmc/linux/drivers/media/dvb-frontends/si21xx.c (revision 9a0bf528b4d66b605f02634236da085595c22101)
1*9a0bf528SMauro Carvalho Chehab /* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
2*9a0bf528SMauro Carvalho Chehab *
3*9a0bf528SMauro Carvalho Chehab * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
4*9a0bf528SMauro Carvalho Chehab *
5*9a0bf528SMauro Carvalho Chehab *	This program is free software; you can redistribute it and/or modify
6*9a0bf528SMauro Carvalho Chehab *	it under the terms of the GNU General Public License as published by
7*9a0bf528SMauro Carvalho Chehab *	the Free Software Foundation; either version 2 of the License, or
8*9a0bf528SMauro Carvalho Chehab *	(at your option) any later version.
9*9a0bf528SMauro Carvalho Chehab *
10*9a0bf528SMauro Carvalho Chehab */
11*9a0bf528SMauro Carvalho Chehab #include <linux/init.h>
12*9a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
13*9a0bf528SMauro Carvalho Chehab #include <linux/module.h>
14*9a0bf528SMauro Carvalho Chehab #include <linux/string.h>
15*9a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
16*9a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
17*9a0bf528SMauro Carvalho Chehab #include <asm/div64.h>
18*9a0bf528SMauro Carvalho Chehab 
19*9a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
20*9a0bf528SMauro Carvalho Chehab #include "si21xx.h"
21*9a0bf528SMauro Carvalho Chehab 
22*9a0bf528SMauro Carvalho Chehab #define	REVISION_REG			0x00
23*9a0bf528SMauro Carvalho Chehab #define	SYSTEM_MODE_REG			0x01
24*9a0bf528SMauro Carvalho Chehab #define	TS_CTRL_REG_1			0x02
25*9a0bf528SMauro Carvalho Chehab #define	TS_CTRL_REG_2			0x03
26*9a0bf528SMauro Carvalho Chehab #define	PIN_CTRL_REG_1			0x04
27*9a0bf528SMauro Carvalho Chehab #define	PIN_CTRL_REG_2			0x05
28*9a0bf528SMauro Carvalho Chehab #define	LOCK_STATUS_REG_1		0x0f
29*9a0bf528SMauro Carvalho Chehab #define	LOCK_STATUS_REG_2		0x10
30*9a0bf528SMauro Carvalho Chehab #define	ACQ_STATUS_REG			0x11
31*9a0bf528SMauro Carvalho Chehab #define	ACQ_CTRL_REG_1			0x13
32*9a0bf528SMauro Carvalho Chehab #define	ACQ_CTRL_REG_2			0x14
33*9a0bf528SMauro Carvalho Chehab #define	PLL_DIVISOR_REG			0x15
34*9a0bf528SMauro Carvalho Chehab #define	COARSE_TUNE_REG			0x16
35*9a0bf528SMauro Carvalho Chehab #define	FINE_TUNE_REG_L			0x17
36*9a0bf528SMauro Carvalho Chehab #define	FINE_TUNE_REG_H			0x18
37*9a0bf528SMauro Carvalho Chehab 
38*9a0bf528SMauro Carvalho Chehab #define	ANALOG_AGC_POWER_LEVEL_REG	0x28
39*9a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_1	0x29
40*9a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_2	0x2a
41*9a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_CTRL_REG_3	0x2b
42*9a0bf528SMauro Carvalho Chehab 
43*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_L		0x31
44*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_M		0x32
45*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATE_REG_H		0x33
46*9a0bf528SMauro Carvalho Chehab 
47*9a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_OFFSET_REG_L	0x36
48*9a0bf528SMauro Carvalho Chehab #define	CFO_ESTIMATOR_OFFSET_REG_H	0x37
49*9a0bf528SMauro Carvalho Chehab #define	CFO_ERROR_REG_L			0x38
50*9a0bf528SMauro Carvalho Chehab #define	CFO_ERROR_REG_H			0x39
51*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_CTRL_REG	0x3a
52*9a0bf528SMauro Carvalho Chehab 
53*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_L			0x3f
54*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_M			0x40
55*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_REG_H			0x41
56*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_MAXIMUM_REG	0x42
57*9a0bf528SMauro Carvalho Chehab #define	SYM_RATE_ESTIMATOR_MINIMUM_REG	0x43
58*9a0bf528SMauro Carvalho Chehab 
59*9a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_CTRL_REG		0x7c
60*9a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_THRSHLD_REG	0x7d
61*9a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_LEVEL_REG_L	0x7e
62*9a0bf528SMauro Carvalho Chehab #define	C_N_ESTIMATOR_LEVEL_REG_H	0x7f
63*9a0bf528SMauro Carvalho Chehab 
64*9a0bf528SMauro Carvalho Chehab #define	BLIND_SCAN_CTRL_REG		0x80
65*9a0bf528SMauro Carvalho Chehab 
66*9a0bf528SMauro Carvalho Chehab #define	LSA_CTRL_REG_1			0x8D
67*9a0bf528SMauro Carvalho Chehab #define	SPCTRM_TILT_CORR_THRSHLD_REG	0x8f
68*9a0bf528SMauro Carvalho Chehab #define	ONE_DB_BNDWDTH_THRSHLD_REG	0x90
69*9a0bf528SMauro Carvalho Chehab #define	TWO_DB_BNDWDTH_THRSHLD_REG	0x91
70*9a0bf528SMauro Carvalho Chehab #define	THREE_DB_BNDWDTH_THRSHLD_REG	0x92
71*9a0bf528SMauro Carvalho Chehab #define	INBAND_POWER_THRSHLD_REG	0x93
72*9a0bf528SMauro Carvalho Chehab #define	REF_NOISE_LVL_MRGN_THRSHLD_REG	0x94
73*9a0bf528SMauro Carvalho Chehab 
74*9a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_1		0xa0
75*9a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_2		0xa1
76*9a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_CTRL_REG_3		0xa2
77*9a0bf528SMauro Carvalho Chehab #define	VIT_SRCH_STATUS_REG		0xa3
78*9a0bf528SMauro Carvalho Chehab #define	VITERBI_BER_COUNT_REG_L		0xab
79*9a0bf528SMauro Carvalho Chehab #define	REED_SOLOMON_CTRL_REG		0xb0
80*9a0bf528SMauro Carvalho Chehab #define	REED_SOLOMON_ERROR_COUNT_REG_L	0xb1
81*9a0bf528SMauro Carvalho Chehab #define	PRBS_CTRL_REG			0xb5
82*9a0bf528SMauro Carvalho Chehab 
83*9a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_1			0xc0
84*9a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_2			0xc1
85*9a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_3			0xc2
86*9a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_REG_4			0xc3
87*9a0bf528SMauro Carvalho Chehab #define	LNB_CTRL_STATUS_REG		0xc4
88*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_0			0xc5
89*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_1			0xc6
90*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_2			0xc7
91*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_3			0xc8
92*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_4			0xc9
93*9a0bf528SMauro Carvalho Chehab #define	LNB_FIFO_REGS_5			0xca
94*9a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_1		0xcb
95*9a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_2		0xcc
96*9a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_3		0xcd
97*9a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_CTRL_REG_4		0xce
98*9a0bf528SMauro Carvalho Chehab #define	LNB_SUPPLY_STATUS_REG		0xcf
99*9a0bf528SMauro Carvalho Chehab 
100*9a0bf528SMauro Carvalho Chehab #define FAIL	-1
101*9a0bf528SMauro Carvalho Chehab #define PASS	0
102*9a0bf528SMauro Carvalho Chehab 
103*9a0bf528SMauro Carvalho Chehab #define ALLOWABLE_FS_COUNT	10
104*9a0bf528SMauro Carvalho Chehab #define STATUS_BER		0
105*9a0bf528SMauro Carvalho Chehab #define STATUS_UCBLOCKS		1
106*9a0bf528SMauro Carvalho Chehab 
107*9a0bf528SMauro Carvalho Chehab static int debug;
108*9a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
109*9a0bf528SMauro Carvalho Chehab 	do { \
110*9a0bf528SMauro Carvalho Chehab 		if (debug) \
111*9a0bf528SMauro Carvalho Chehab 			printk(KERN_DEBUG "si21xx: " args); \
112*9a0bf528SMauro Carvalho Chehab 	} while (0)
113*9a0bf528SMauro Carvalho Chehab 
114*9a0bf528SMauro Carvalho Chehab enum {
115*9a0bf528SMauro Carvalho Chehab 	ACTIVE_HIGH,
116*9a0bf528SMauro Carvalho Chehab 	ACTIVE_LOW
117*9a0bf528SMauro Carvalho Chehab };
118*9a0bf528SMauro Carvalho Chehab enum {
119*9a0bf528SMauro Carvalho Chehab 	BYTE_WIDE,
120*9a0bf528SMauro Carvalho Chehab 	BIT_WIDE
121*9a0bf528SMauro Carvalho Chehab };
122*9a0bf528SMauro Carvalho Chehab enum {
123*9a0bf528SMauro Carvalho Chehab 	CLK_GAPPED_MODE,
124*9a0bf528SMauro Carvalho Chehab 	CLK_CONTINUOUS_MODE
125*9a0bf528SMauro Carvalho Chehab };
126*9a0bf528SMauro Carvalho Chehab enum {
127*9a0bf528SMauro Carvalho Chehab 	RISING_EDGE,
128*9a0bf528SMauro Carvalho Chehab 	FALLING_EDGE
129*9a0bf528SMauro Carvalho Chehab };
130*9a0bf528SMauro Carvalho Chehab enum {
131*9a0bf528SMauro Carvalho Chehab 	MSB_FIRST,
132*9a0bf528SMauro Carvalho Chehab 	LSB_FIRST
133*9a0bf528SMauro Carvalho Chehab };
134*9a0bf528SMauro Carvalho Chehab enum {
135*9a0bf528SMauro Carvalho Chehab 	SERIAL,
136*9a0bf528SMauro Carvalho Chehab 	PARALLEL
137*9a0bf528SMauro Carvalho Chehab };
138*9a0bf528SMauro Carvalho Chehab 
139*9a0bf528SMauro Carvalho Chehab struct si21xx_state {
140*9a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
141*9a0bf528SMauro Carvalho Chehab 	const struct si21xx_config *config;
142*9a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
143*9a0bf528SMauro Carvalho Chehab 	u8 initialised:1;
144*9a0bf528SMauro Carvalho Chehab 	int errmode;
145*9a0bf528SMauro Carvalho Chehab 	int fs;			/*Sampling rate of the ADC in MHz*/
146*9a0bf528SMauro Carvalho Chehab };
147*9a0bf528SMauro Carvalho Chehab 
148*9a0bf528SMauro Carvalho Chehab /*	register default initialization */
149*9a0bf528SMauro Carvalho Chehab static u8 serit_sp1511lhb_inittab[] = {
150*9a0bf528SMauro Carvalho Chehab 	0x01, 0x28,	/* set i2c_inc_disable */
151*9a0bf528SMauro Carvalho Chehab 	0x20, 0x03,
152*9a0bf528SMauro Carvalho Chehab 	0x27, 0x20,
153*9a0bf528SMauro Carvalho Chehab 	0xe0, 0x45,
154*9a0bf528SMauro Carvalho Chehab 	0xe1, 0x08,
155*9a0bf528SMauro Carvalho Chehab 	0xfe, 0x01,
156*9a0bf528SMauro Carvalho Chehab 	0x01, 0x28,
157*9a0bf528SMauro Carvalho Chehab 	0x89, 0x09,
158*9a0bf528SMauro Carvalho Chehab 	0x04, 0x80,
159*9a0bf528SMauro Carvalho Chehab 	0x05, 0x01,
160*9a0bf528SMauro Carvalho Chehab 	0x06, 0x00,
161*9a0bf528SMauro Carvalho Chehab 	0x20, 0x03,
162*9a0bf528SMauro Carvalho Chehab 	0x24, 0x88,
163*9a0bf528SMauro Carvalho Chehab 	0x29, 0x09,
164*9a0bf528SMauro Carvalho Chehab 	0x2a, 0x0f,
165*9a0bf528SMauro Carvalho Chehab 	0x2c, 0x10,
166*9a0bf528SMauro Carvalho Chehab 	0x2d, 0x19,
167*9a0bf528SMauro Carvalho Chehab 	0x2e, 0x08,
168*9a0bf528SMauro Carvalho Chehab 	0x2f, 0x10,
169*9a0bf528SMauro Carvalho Chehab 	0x30, 0x19,
170*9a0bf528SMauro Carvalho Chehab 	0x34, 0x20,
171*9a0bf528SMauro Carvalho Chehab 	0x35, 0x03,
172*9a0bf528SMauro Carvalho Chehab 	0x45, 0x02,
173*9a0bf528SMauro Carvalho Chehab 	0x46, 0x45,
174*9a0bf528SMauro Carvalho Chehab 	0x47, 0xd0,
175*9a0bf528SMauro Carvalho Chehab 	0x48, 0x00,
176*9a0bf528SMauro Carvalho Chehab 	0x49, 0x40,
177*9a0bf528SMauro Carvalho Chehab 	0x4a, 0x03,
178*9a0bf528SMauro Carvalho Chehab 	0x4c, 0xfd,
179*9a0bf528SMauro Carvalho Chehab 	0x4f, 0x2e,
180*9a0bf528SMauro Carvalho Chehab 	0x50, 0x2e,
181*9a0bf528SMauro Carvalho Chehab 	0x51, 0x10,
182*9a0bf528SMauro Carvalho Chehab 	0x52, 0x10,
183*9a0bf528SMauro Carvalho Chehab 	0x56, 0x92,
184*9a0bf528SMauro Carvalho Chehab 	0x59, 0x00,
185*9a0bf528SMauro Carvalho Chehab 	0x5a, 0x2d,
186*9a0bf528SMauro Carvalho Chehab 	0x5b, 0x33,
187*9a0bf528SMauro Carvalho Chehab 	0x5c, 0x1f,
188*9a0bf528SMauro Carvalho Chehab 	0x5f, 0x76,
189*9a0bf528SMauro Carvalho Chehab 	0x62, 0xc0,
190*9a0bf528SMauro Carvalho Chehab 	0x63, 0xc0,
191*9a0bf528SMauro Carvalho Chehab 	0x64, 0xf3,
192*9a0bf528SMauro Carvalho Chehab 	0x65, 0xf3,
193*9a0bf528SMauro Carvalho Chehab 	0x79, 0x40,
194*9a0bf528SMauro Carvalho Chehab 	0x6a, 0x40,
195*9a0bf528SMauro Carvalho Chehab 	0x6b, 0x0a,
196*9a0bf528SMauro Carvalho Chehab 	0x6c, 0x80,
197*9a0bf528SMauro Carvalho Chehab 	0x6d, 0x27,
198*9a0bf528SMauro Carvalho Chehab 	0x71, 0x06,
199*9a0bf528SMauro Carvalho Chehab 	0x75, 0x60,
200*9a0bf528SMauro Carvalho Chehab 	0x78, 0x00,
201*9a0bf528SMauro Carvalho Chehab 	0x79, 0xb5,
202*9a0bf528SMauro Carvalho Chehab 	0x7c, 0x05,
203*9a0bf528SMauro Carvalho Chehab 	0x7d, 0x1a,
204*9a0bf528SMauro Carvalho Chehab 	0x87, 0x55,
205*9a0bf528SMauro Carvalho Chehab 	0x88, 0x72,
206*9a0bf528SMauro Carvalho Chehab 	0x8f, 0x08,
207*9a0bf528SMauro Carvalho Chehab 	0x90, 0xe0,
208*9a0bf528SMauro Carvalho Chehab 	0x94, 0x40,
209*9a0bf528SMauro Carvalho Chehab 	0xa0, 0x3f,
210*9a0bf528SMauro Carvalho Chehab 	0xa1, 0xc0,
211*9a0bf528SMauro Carvalho Chehab 	0xa4, 0xcc,
212*9a0bf528SMauro Carvalho Chehab 	0xa5, 0x66,
213*9a0bf528SMauro Carvalho Chehab 	0xa6, 0x66,
214*9a0bf528SMauro Carvalho Chehab 	0xa7, 0x7b,
215*9a0bf528SMauro Carvalho Chehab 	0xa8, 0x7b,
216*9a0bf528SMauro Carvalho Chehab 	0xa9, 0x7b,
217*9a0bf528SMauro Carvalho Chehab 	0xaa, 0x9a,
218*9a0bf528SMauro Carvalho Chehab 	0xed, 0x04,
219*9a0bf528SMauro Carvalho Chehab 	0xad, 0x00,
220*9a0bf528SMauro Carvalho Chehab 	0xae, 0x03,
221*9a0bf528SMauro Carvalho Chehab 	0xcc, 0xab,
222*9a0bf528SMauro Carvalho Chehab 	0x01, 0x08,
223*9a0bf528SMauro Carvalho Chehab 	0xff, 0xff
224*9a0bf528SMauro Carvalho Chehab };
225*9a0bf528SMauro Carvalho Chehab 
226*9a0bf528SMauro Carvalho Chehab /*	low level read/writes */
227*9a0bf528SMauro Carvalho Chehab static int si21_writeregs(struct si21xx_state *state, u8 reg1,
228*9a0bf528SMauro Carvalho Chehab 							u8 *data, int len)
229*9a0bf528SMauro Carvalho Chehab {
230*9a0bf528SMauro Carvalho Chehab 	int ret;
231*9a0bf528SMauro Carvalho Chehab 	u8 buf[60];/* = { reg1, data };*/
232*9a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
233*9a0bf528SMauro Carvalho Chehab 				.addr = state->config->demod_address,
234*9a0bf528SMauro Carvalho Chehab 				.flags = 0,
235*9a0bf528SMauro Carvalho Chehab 				.buf = buf,
236*9a0bf528SMauro Carvalho Chehab 				.len = len + 1
237*9a0bf528SMauro Carvalho Chehab 	};
238*9a0bf528SMauro Carvalho Chehab 
239*9a0bf528SMauro Carvalho Chehab 	msg.buf[0] =  reg1;
240*9a0bf528SMauro Carvalho Chehab 	memcpy(msg.buf + 1, data, len);
241*9a0bf528SMauro Carvalho Chehab 
242*9a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
243*9a0bf528SMauro Carvalho Chehab 
244*9a0bf528SMauro Carvalho Chehab 	if (ret != 1)
245*9a0bf528SMauro Carvalho Chehab 		dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, "
246*9a0bf528SMauro Carvalho Chehab 			"ret == %i)\n", __func__, reg1, data[0], ret);
247*9a0bf528SMauro Carvalho Chehab 
248*9a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
249*9a0bf528SMauro Carvalho Chehab }
250*9a0bf528SMauro Carvalho Chehab 
251*9a0bf528SMauro Carvalho Chehab static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
252*9a0bf528SMauro Carvalho Chehab {
253*9a0bf528SMauro Carvalho Chehab 	int ret;
254*9a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
255*9a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
256*9a0bf528SMauro Carvalho Chehab 				.addr = state->config->demod_address,
257*9a0bf528SMauro Carvalho Chehab 				.flags = 0,
258*9a0bf528SMauro Carvalho Chehab 				.buf = buf,
259*9a0bf528SMauro Carvalho Chehab 				.len = 2
260*9a0bf528SMauro Carvalho Chehab 	};
261*9a0bf528SMauro Carvalho Chehab 
262*9a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
263*9a0bf528SMauro Carvalho Chehab 
264*9a0bf528SMauro Carvalho Chehab 	if (ret != 1)
265*9a0bf528SMauro Carvalho Chehab 		dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, "
266*9a0bf528SMauro Carvalho Chehab 			"ret == %i)\n", __func__, reg, data, ret);
267*9a0bf528SMauro Carvalho Chehab 
268*9a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
269*9a0bf528SMauro Carvalho Chehab }
270*9a0bf528SMauro Carvalho Chehab 
271*9a0bf528SMauro Carvalho Chehab static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len)
272*9a0bf528SMauro Carvalho Chehab {
273*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
274*9a0bf528SMauro Carvalho Chehab 
275*9a0bf528SMauro Carvalho Chehab 	if (len != 2)
276*9a0bf528SMauro Carvalho Chehab 		return -EINVAL;
277*9a0bf528SMauro Carvalho Chehab 
278*9a0bf528SMauro Carvalho Chehab 	return si21_writereg(state, buf[0], buf[1]);
279*9a0bf528SMauro Carvalho Chehab }
280*9a0bf528SMauro Carvalho Chehab 
281*9a0bf528SMauro Carvalho Chehab static u8 si21_readreg(struct si21xx_state *state, u8 reg)
282*9a0bf528SMauro Carvalho Chehab {
283*9a0bf528SMauro Carvalho Chehab 	int ret;
284*9a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
285*9a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
286*9a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
287*9a0bf528SMauro Carvalho Chehab 		{
288*9a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
289*9a0bf528SMauro Carvalho Chehab 			.flags = 0,
290*9a0bf528SMauro Carvalho Chehab 			.buf = b0,
291*9a0bf528SMauro Carvalho Chehab 			.len = 1
292*9a0bf528SMauro Carvalho Chehab 		}, {
293*9a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
294*9a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
295*9a0bf528SMauro Carvalho Chehab 			.buf = b1,
296*9a0bf528SMauro Carvalho Chehab 			.len = 1
297*9a0bf528SMauro Carvalho Chehab 		}
298*9a0bf528SMauro Carvalho Chehab 	};
299*9a0bf528SMauro Carvalho Chehab 
300*9a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
301*9a0bf528SMauro Carvalho Chehab 
302*9a0bf528SMauro Carvalho Chehab 	if (ret != 2)
303*9a0bf528SMauro Carvalho Chehab 		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
304*9a0bf528SMauro Carvalho Chehab 			__func__, reg, ret);
305*9a0bf528SMauro Carvalho Chehab 
306*9a0bf528SMauro Carvalho Chehab 	return b1[0];
307*9a0bf528SMauro Carvalho Chehab }
308*9a0bf528SMauro Carvalho Chehab 
309*9a0bf528SMauro Carvalho Chehab static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
310*9a0bf528SMauro Carvalho Chehab {
311*9a0bf528SMauro Carvalho Chehab 	int ret;
312*9a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
313*9a0bf528SMauro Carvalho Chehab 		{
314*9a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
315*9a0bf528SMauro Carvalho Chehab 			.flags = 0,
316*9a0bf528SMauro Carvalho Chehab 			.buf = &reg1,
317*9a0bf528SMauro Carvalho Chehab 			.len = 1
318*9a0bf528SMauro Carvalho Chehab 		}, {
319*9a0bf528SMauro Carvalho Chehab 			.addr = state->config->demod_address,
320*9a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
321*9a0bf528SMauro Carvalho Chehab 			.buf = b,
322*9a0bf528SMauro Carvalho Chehab 			.len = len
323*9a0bf528SMauro Carvalho Chehab 		}
324*9a0bf528SMauro Carvalho Chehab 	};
325*9a0bf528SMauro Carvalho Chehab 
326*9a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
327*9a0bf528SMauro Carvalho Chehab 
328*9a0bf528SMauro Carvalho Chehab 	if (ret != 2)
329*9a0bf528SMauro Carvalho Chehab 		dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
330*9a0bf528SMauro Carvalho Chehab 
331*9a0bf528SMauro Carvalho Chehab 	return ret == 2 ? 0 : -1;
332*9a0bf528SMauro Carvalho Chehab }
333*9a0bf528SMauro Carvalho Chehab 
334*9a0bf528SMauro Carvalho Chehab static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
335*9a0bf528SMauro Carvalho Chehab {
336*9a0bf528SMauro Carvalho Chehab 	unsigned long start = jiffies;
337*9a0bf528SMauro Carvalho Chehab 
338*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
339*9a0bf528SMauro Carvalho Chehab 
340*9a0bf528SMauro Carvalho Chehab 	while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) {
341*9a0bf528SMauro Carvalho Chehab 		if (jiffies - start > timeout) {
342*9a0bf528SMauro Carvalho Chehab 			dprintk("%s: timeout!!\n", __func__);
343*9a0bf528SMauro Carvalho Chehab 			return -ETIMEDOUT;
344*9a0bf528SMauro Carvalho Chehab 		}
345*9a0bf528SMauro Carvalho Chehab 		msleep(10);
346*9a0bf528SMauro Carvalho Chehab 	};
347*9a0bf528SMauro Carvalho Chehab 
348*9a0bf528SMauro Carvalho Chehab 	return 0;
349*9a0bf528SMauro Carvalho Chehab }
350*9a0bf528SMauro Carvalho Chehab 
351*9a0bf528SMauro Carvalho Chehab static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
352*9a0bf528SMauro Carvalho Chehab {
353*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
354*9a0bf528SMauro Carvalho Chehab 	u32 sym_rate, data_rate;
355*9a0bf528SMauro Carvalho Chehab 	int i;
356*9a0bf528SMauro Carvalho Chehab 	u8 sym_rate_bytes[3];
357*9a0bf528SMauro Carvalho Chehab 
358*9a0bf528SMauro Carvalho Chehab 	dprintk("%s : srate = %i\n", __func__ , srate);
359*9a0bf528SMauro Carvalho Chehab 
360*9a0bf528SMauro Carvalho Chehab 	if ((srate < 1000000) || (srate > 45000000))
361*9a0bf528SMauro Carvalho Chehab 		return -EINVAL;
362*9a0bf528SMauro Carvalho Chehab 
363*9a0bf528SMauro Carvalho Chehab 	data_rate = srate;
364*9a0bf528SMauro Carvalho Chehab 	sym_rate = 0;
365*9a0bf528SMauro Carvalho Chehab 
366*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 4; ++i) {
367*9a0bf528SMauro Carvalho Chehab 		sym_rate /= 100;
368*9a0bf528SMauro Carvalho Chehab 		sym_rate = sym_rate + ((data_rate % 100) * 0x800000) /
369*9a0bf528SMauro Carvalho Chehab 								state->fs;
370*9a0bf528SMauro Carvalho Chehab 		data_rate /= 100;
371*9a0bf528SMauro Carvalho Chehab 	}
372*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 3; ++i)
373*9a0bf528SMauro Carvalho Chehab 		sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);
374*9a0bf528SMauro Carvalho Chehab 
375*9a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);
376*9a0bf528SMauro Carvalho Chehab 
377*9a0bf528SMauro Carvalho Chehab 	return 0;
378*9a0bf528SMauro Carvalho Chehab }
379*9a0bf528SMauro Carvalho Chehab 
380*9a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
381*9a0bf528SMauro Carvalho Chehab 					struct dvb_diseqc_master_cmd *m)
382*9a0bf528SMauro Carvalho Chehab {
383*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
384*9a0bf528SMauro Carvalho Chehab 	u8 lnb_status;
385*9a0bf528SMauro Carvalho Chehab 	u8 LNB_CTRL_1;
386*9a0bf528SMauro Carvalho Chehab 	int status;
387*9a0bf528SMauro Carvalho Chehab 
388*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
389*9a0bf528SMauro Carvalho Chehab 
390*9a0bf528SMauro Carvalho Chehab 	status = PASS;
391*9a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 = 0;
392*9a0bf528SMauro Carvalho Chehab 
393*9a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01);
394*9a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);
395*9a0bf528SMauro Carvalho Chehab 
396*9a0bf528SMauro Carvalho Chehab 	/*fill the FIFO*/
397*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
398*9a0bf528SMauro Carvalho Chehab 
399*9a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 = (lnb_status & 0x70);
400*9a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 |= m->msg_len;
401*9a0bf528SMauro Carvalho Chehab 
402*9a0bf528SMauro Carvalho Chehab 	LNB_CTRL_1 |= 0x80;	/* begin LNB signaling */
403*9a0bf528SMauro Carvalho Chehab 
404*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);
405*9a0bf528SMauro Carvalho Chehab 
406*9a0bf528SMauro Carvalho Chehab 	return status;
407*9a0bf528SMauro Carvalho Chehab }
408*9a0bf528SMauro Carvalho Chehab 
409*9a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
410*9a0bf528SMauro Carvalho Chehab 						fe_sec_mini_cmd_t burst)
411*9a0bf528SMauro Carvalho Chehab {
412*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
413*9a0bf528SMauro Carvalho Chehab 	u8 val;
414*9a0bf528SMauro Carvalho Chehab 
415*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
416*9a0bf528SMauro Carvalho Chehab 
417*9a0bf528SMauro Carvalho Chehab 	if (si21xx_wait_diseqc_idle(state, 100) < 0)
418*9a0bf528SMauro Carvalho Chehab 		return -ETIMEDOUT;
419*9a0bf528SMauro Carvalho Chehab 
420*9a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, 0xc1));
421*9a0bf528SMauro Carvalho Chehab 	if (si21_writereg(state, LNB_CTRL_REG_1,
422*9a0bf528SMauro Carvalho Chehab 			burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
423*9a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
424*9a0bf528SMauro Carvalho Chehab 
425*9a0bf528SMauro Carvalho Chehab 	if (si21xx_wait_diseqc_idle(state, 100) < 0)
426*9a0bf528SMauro Carvalho Chehab 		return -ETIMEDOUT;
427*9a0bf528SMauro Carvalho Chehab 
428*9a0bf528SMauro Carvalho Chehab 	if (si21_writereg(state, LNB_CTRL_REG_1, val))
429*9a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
430*9a0bf528SMauro Carvalho Chehab 
431*9a0bf528SMauro Carvalho Chehab 	return 0;
432*9a0bf528SMauro Carvalho Chehab }
433*9a0bf528SMauro Carvalho Chehab /*	30.06.2008 */
434*9a0bf528SMauro Carvalho Chehab static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
435*9a0bf528SMauro Carvalho Chehab {
436*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
437*9a0bf528SMauro Carvalho Chehab 	u8 val;
438*9a0bf528SMauro Carvalho Chehab 
439*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
440*9a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
441*9a0bf528SMauro Carvalho Chehab 
442*9a0bf528SMauro Carvalho Chehab 	switch (tone) {
443*9a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
444*9a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);
445*9a0bf528SMauro Carvalho Chehab 
446*9a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
447*9a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));
448*9a0bf528SMauro Carvalho Chehab 
449*9a0bf528SMauro Carvalho Chehab 	default:
450*9a0bf528SMauro Carvalho Chehab 		return -EINVAL;
451*9a0bf528SMauro Carvalho Chehab 	}
452*9a0bf528SMauro Carvalho Chehab }
453*9a0bf528SMauro Carvalho Chehab 
454*9a0bf528SMauro Carvalho Chehab static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
455*9a0bf528SMauro Carvalho Chehab {
456*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
457*9a0bf528SMauro Carvalho Chehab 
458*9a0bf528SMauro Carvalho Chehab 	u8 val;
459*9a0bf528SMauro Carvalho Chehab 	dprintk("%s: %s\n", __func__,
460*9a0bf528SMauro Carvalho Chehab 		volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
461*9a0bf528SMauro Carvalho Chehab 		volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
462*9a0bf528SMauro Carvalho Chehab 
463*9a0bf528SMauro Carvalho Chehab 
464*9a0bf528SMauro Carvalho Chehab 	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
465*9a0bf528SMauro Carvalho Chehab 
466*9a0bf528SMauro Carvalho Chehab 	switch (volt) {
467*9a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
468*9a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
469*9a0bf528SMauro Carvalho Chehab 		break;
470*9a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
471*9a0bf528SMauro Carvalho Chehab 		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
472*9a0bf528SMauro Carvalho Chehab 		break;
473*9a0bf528SMauro Carvalho Chehab 	default:
474*9a0bf528SMauro Carvalho Chehab 		return -EINVAL;
475*9a0bf528SMauro Carvalho Chehab 	};
476*9a0bf528SMauro Carvalho Chehab }
477*9a0bf528SMauro Carvalho Chehab 
478*9a0bf528SMauro Carvalho Chehab static int si21xx_init(struct dvb_frontend *fe)
479*9a0bf528SMauro Carvalho Chehab {
480*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
481*9a0bf528SMauro Carvalho Chehab 	int i;
482*9a0bf528SMauro Carvalho Chehab 	int status = 0;
483*9a0bf528SMauro Carvalho Chehab 	u8 reg1;
484*9a0bf528SMauro Carvalho Chehab 	u8 val;
485*9a0bf528SMauro Carvalho Chehab 	u8 reg2[2];
486*9a0bf528SMauro Carvalho Chehab 
487*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
488*9a0bf528SMauro Carvalho Chehab 
489*9a0bf528SMauro Carvalho Chehab 	for (i = 0; ; i += 2) {
490*9a0bf528SMauro Carvalho Chehab 		reg1 = serit_sp1511lhb_inittab[i];
491*9a0bf528SMauro Carvalho Chehab 		val = serit_sp1511lhb_inittab[i+1];
492*9a0bf528SMauro Carvalho Chehab 		if (reg1 == 0xff && val == 0xff)
493*9a0bf528SMauro Carvalho Chehab 			break;
494*9a0bf528SMauro Carvalho Chehab 		si21_writeregs(state, reg1, &val, 1);
495*9a0bf528SMauro Carvalho Chehab 	}
496*9a0bf528SMauro Carvalho Chehab 
497*9a0bf528SMauro Carvalho Chehab 	/*DVB QPSK SYSTEM MODE REG*/
498*9a0bf528SMauro Carvalho Chehab 	reg1 = 0x08;
499*9a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYSTEM_MODE_REG, &reg1, 0x01);
500*9a0bf528SMauro Carvalho Chehab 
501*9a0bf528SMauro Carvalho Chehab 	/*transport stream config*/
502*9a0bf528SMauro Carvalho Chehab 	/*
503*9a0bf528SMauro Carvalho Chehab 	mode = PARALLEL;
504*9a0bf528SMauro Carvalho Chehab 	sdata_form = LSB_FIRST;
505*9a0bf528SMauro Carvalho Chehab 	clk_edge = FALLING_EDGE;
506*9a0bf528SMauro Carvalho Chehab 	clk_mode = CLK_GAPPED_MODE;
507*9a0bf528SMauro Carvalho Chehab 	strt_len = BYTE_WIDE;
508*9a0bf528SMauro Carvalho Chehab 	sync_pol = ACTIVE_HIGH;
509*9a0bf528SMauro Carvalho Chehab 	val_pol = ACTIVE_HIGH;
510*9a0bf528SMauro Carvalho Chehab 	err_pol = ACTIVE_HIGH;
511*9a0bf528SMauro Carvalho Chehab 	sclk_rate = 0x00;
512*9a0bf528SMauro Carvalho Chehab 	parity = 0x00 ;
513*9a0bf528SMauro Carvalho Chehab 	data_delay = 0x00;
514*9a0bf528SMauro Carvalho Chehab 	clk_delay = 0x00;
515*9a0bf528SMauro Carvalho Chehab 	pclk_smooth = 0x00;
516*9a0bf528SMauro Carvalho Chehab 	*/
517*9a0bf528SMauro Carvalho Chehab 	reg2[0] =
518*9a0bf528SMauro Carvalho Chehab 		PARALLEL + (LSB_FIRST << 1)
519*9a0bf528SMauro Carvalho Chehab 		+ (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3)
520*9a0bf528SMauro Carvalho Chehab 		+ (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5)
521*9a0bf528SMauro Carvalho Chehab 		+ (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7);
522*9a0bf528SMauro Carvalho Chehab 
523*9a0bf528SMauro Carvalho Chehab 	reg2[1] = 0;
524*9a0bf528SMauro Carvalho Chehab 	/*	sclk_rate + (parity << 2)
525*9a0bf528SMauro Carvalho Chehab 		+ (data_delay << 3) + (clk_delay << 4)
526*9a0bf528SMauro Carvalho Chehab 		+ (pclk_smooth << 5);
527*9a0bf528SMauro Carvalho Chehab 	*/
528*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02);
529*9a0bf528SMauro Carvalho Chehab 	if (status != 0)
530*9a0bf528SMauro Carvalho Chehab 		dprintk(" %s : TS Set Error\n", __func__);
531*9a0bf528SMauro Carvalho Chehab 
532*9a0bf528SMauro Carvalho Chehab 	return 0;
533*9a0bf528SMauro Carvalho Chehab 
534*9a0bf528SMauro Carvalho Chehab }
535*9a0bf528SMauro Carvalho Chehab 
536*9a0bf528SMauro Carvalho Chehab static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status)
537*9a0bf528SMauro Carvalho Chehab {
538*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
539*9a0bf528SMauro Carvalho Chehab 	u8 regs_read[2];
540*9a0bf528SMauro Carvalho Chehab 	u8 reg_read;
541*9a0bf528SMauro Carvalho Chehab 	u8 i;
542*9a0bf528SMauro Carvalho Chehab 	u8 lock;
543*9a0bf528SMauro Carvalho Chehab 	u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
544*9a0bf528SMauro Carvalho Chehab 
545*9a0bf528SMauro Carvalho Chehab 	si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02);
546*9a0bf528SMauro Carvalho Chehab 	reg_read = 0;
547*9a0bf528SMauro Carvalho Chehab 
548*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 7; ++i)
549*9a0bf528SMauro Carvalho Chehab 		reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);
550*9a0bf528SMauro Carvalho Chehab 
551*9a0bf528SMauro Carvalho Chehab 	lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));
552*9a0bf528SMauro Carvalho Chehab 
553*9a0bf528SMauro Carvalho Chehab 	dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
554*9a0bf528SMauro Carvalho Chehab 	*status = 0;
555*9a0bf528SMauro Carvalho Chehab 
556*9a0bf528SMauro Carvalho Chehab 	if (signal > 10)
557*9a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
558*9a0bf528SMauro Carvalho Chehab 
559*9a0bf528SMauro Carvalho Chehab 	if (lock & 0x2)
560*9a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
561*9a0bf528SMauro Carvalho Chehab 
562*9a0bf528SMauro Carvalho Chehab 	if (lock & 0x20)
563*9a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
564*9a0bf528SMauro Carvalho Chehab 
565*9a0bf528SMauro Carvalho Chehab 	if (lock & 0x40)
566*9a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
567*9a0bf528SMauro Carvalho Chehab 
568*9a0bf528SMauro Carvalho Chehab 	if ((lock & 0x7b) == 0x7b)
569*9a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
570*9a0bf528SMauro Carvalho Chehab 
571*9a0bf528SMauro Carvalho Chehab 	return 0;
572*9a0bf528SMauro Carvalho Chehab }
573*9a0bf528SMauro Carvalho Chehab 
574*9a0bf528SMauro Carvalho Chehab static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
575*9a0bf528SMauro Carvalho Chehab {
576*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
577*9a0bf528SMauro Carvalho Chehab 
578*9a0bf528SMauro Carvalho Chehab 	/*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
579*9a0bf528SMauro Carvalho Chehab 						(u8*)agclevel, 0x01);*/
580*9a0bf528SMauro Carvalho Chehab 
581*9a0bf528SMauro Carvalho Chehab 	u16 signal = (3 * si21_readreg(state, 0x27) *
582*9a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x28));
583*9a0bf528SMauro Carvalho Chehab 
584*9a0bf528SMauro Carvalho Chehab 	dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
585*9a0bf528SMauro Carvalho Chehab 		si21_readreg(state, 0x27),
586*9a0bf528SMauro Carvalho Chehab 		si21_readreg(state, 0x28), (int) signal);
587*9a0bf528SMauro Carvalho Chehab 
588*9a0bf528SMauro Carvalho Chehab 	signal  <<= 4;
589*9a0bf528SMauro Carvalho Chehab 	*strength = signal;
590*9a0bf528SMauro Carvalho Chehab 
591*9a0bf528SMauro Carvalho Chehab 	return 0;
592*9a0bf528SMauro Carvalho Chehab }
593*9a0bf528SMauro Carvalho Chehab 
594*9a0bf528SMauro Carvalho Chehab static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
595*9a0bf528SMauro Carvalho Chehab {
596*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
597*9a0bf528SMauro Carvalho Chehab 
598*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
599*9a0bf528SMauro Carvalho Chehab 
600*9a0bf528SMauro Carvalho Chehab 	if (state->errmode != STATUS_BER)
601*9a0bf528SMauro Carvalho Chehab 		return 0;
602*9a0bf528SMauro Carvalho Chehab 
603*9a0bf528SMauro Carvalho Chehab 	*ber = (si21_readreg(state, 0x1d) << 8) |
604*9a0bf528SMauro Carvalho Chehab 				si21_readreg(state, 0x1e);
605*9a0bf528SMauro Carvalho Chehab 
606*9a0bf528SMauro Carvalho Chehab 	return 0;
607*9a0bf528SMauro Carvalho Chehab }
608*9a0bf528SMauro Carvalho Chehab 
609*9a0bf528SMauro Carvalho Chehab static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
610*9a0bf528SMauro Carvalho Chehab {
611*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
612*9a0bf528SMauro Carvalho Chehab 
613*9a0bf528SMauro Carvalho Chehab 	s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
614*9a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x25));
615*9a0bf528SMauro Carvalho Chehab 	xsnr = 3 * (xsnr - 0xa100);
616*9a0bf528SMauro Carvalho Chehab 	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
617*9a0bf528SMauro Carvalho Chehab 
618*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
619*9a0bf528SMauro Carvalho Chehab 
620*9a0bf528SMauro Carvalho Chehab 	return 0;
621*9a0bf528SMauro Carvalho Chehab }
622*9a0bf528SMauro Carvalho Chehab 
623*9a0bf528SMauro Carvalho Chehab static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
624*9a0bf528SMauro Carvalho Chehab {
625*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
626*9a0bf528SMauro Carvalho Chehab 
627*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
628*9a0bf528SMauro Carvalho Chehab 
629*9a0bf528SMauro Carvalho Chehab 	if (state->errmode != STATUS_UCBLOCKS)
630*9a0bf528SMauro Carvalho Chehab 		*ucblocks = 0;
631*9a0bf528SMauro Carvalho Chehab 	else
632*9a0bf528SMauro Carvalho Chehab 		*ucblocks = (si21_readreg(state, 0x1d) << 8) |
633*9a0bf528SMauro Carvalho Chehab 					si21_readreg(state, 0x1e);
634*9a0bf528SMauro Carvalho Chehab 
635*9a0bf528SMauro Carvalho Chehab 	return 0;
636*9a0bf528SMauro Carvalho Chehab }
637*9a0bf528SMauro Carvalho Chehab 
638*9a0bf528SMauro Carvalho Chehab /*	initiates a channel acquisition sequence
639*9a0bf528SMauro Carvalho Chehab 	using the specified symbol rate and code rate */
640*9a0bf528SMauro Carvalho Chehab static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
641*9a0bf528SMauro Carvalho Chehab 						fe_code_rate_t crate)
642*9a0bf528SMauro Carvalho Chehab {
643*9a0bf528SMauro Carvalho Chehab 
644*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
645*9a0bf528SMauro Carvalho Chehab 	u8 coderates[] = {
646*9a0bf528SMauro Carvalho Chehab 				0x0, 0x01, 0x02, 0x04, 0x00,
647*9a0bf528SMauro Carvalho Chehab 				0x8, 0x10, 0x20, 0x00, 0x3f
648*9a0bf528SMauro Carvalho Chehab 	};
649*9a0bf528SMauro Carvalho Chehab 
650*9a0bf528SMauro Carvalho Chehab 	u8 coderate_ptr;
651*9a0bf528SMauro Carvalho Chehab 	int status;
652*9a0bf528SMauro Carvalho Chehab 	u8 start_acq = 0x80;
653*9a0bf528SMauro Carvalho Chehab 	u8 reg, regs[3];
654*9a0bf528SMauro Carvalho Chehab 
655*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
656*9a0bf528SMauro Carvalho Chehab 
657*9a0bf528SMauro Carvalho Chehab 	status = PASS;
658*9a0bf528SMauro Carvalho Chehab 	coderate_ptr = coderates[crate];
659*9a0bf528SMauro Carvalho Chehab 
660*9a0bf528SMauro Carvalho Chehab 	si21xx_set_symbolrate(fe, symbrate);
661*9a0bf528SMauro Carvalho Chehab 
662*9a0bf528SMauro Carvalho Chehab 	/* write code rates to use in the Viterbi search */
663*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
664*9a0bf528SMauro Carvalho Chehab 				VIT_SRCH_CTRL_REG_1,
665*9a0bf528SMauro Carvalho Chehab 				&coderate_ptr, 0x01);
666*9a0bf528SMauro Carvalho Chehab 
667*9a0bf528SMauro Carvalho Chehab 	/* clear acq_start bit */
668*9a0bf528SMauro Carvalho Chehab 	status |= si21_readregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
669*9a0bf528SMauro Carvalho Chehab 	reg &= ~start_acq;
670*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
671*9a0bf528SMauro Carvalho Chehab 
672*9a0bf528SMauro Carvalho Chehab 	/* use new Carrier Frequency Offset Estimator (QuickLock) */
673*9a0bf528SMauro Carvalho Chehab 	regs[0] = 0xCB;
674*9a0bf528SMauro Carvalho Chehab 	regs[1] = 0x40;
675*9a0bf528SMauro Carvalho Chehab 	regs[2] = 0xCB;
676*9a0bf528SMauro Carvalho Chehab 
677*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
678*9a0bf528SMauro Carvalho Chehab 				TWO_DB_BNDWDTH_THRSHLD_REG,
679*9a0bf528SMauro Carvalho Chehab 				&regs[0], 0x03);
680*9a0bf528SMauro Carvalho Chehab 	reg = 0x56;
681*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
682*9a0bf528SMauro Carvalho Chehab 				LSA_CTRL_REG_1, &reg, 1);
683*9a0bf528SMauro Carvalho Chehab 	reg = 0x05;
684*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
685*9a0bf528SMauro Carvalho Chehab 				BLIND_SCAN_CTRL_REG, &reg, 1);
686*9a0bf528SMauro Carvalho Chehab 	/* start automatic acq */
687*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state,
688*9a0bf528SMauro Carvalho Chehab 				ACQ_CTRL_REG_2, &start_acq, 0x01);
689*9a0bf528SMauro Carvalho Chehab 
690*9a0bf528SMauro Carvalho Chehab 	return status;
691*9a0bf528SMauro Carvalho Chehab }
692*9a0bf528SMauro Carvalho Chehab 
693*9a0bf528SMauro Carvalho Chehab static int si21xx_set_frontend(struct dvb_frontend *fe)
694*9a0bf528SMauro Carvalho Chehab {
695*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
696*9a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
697*9a0bf528SMauro Carvalho Chehab 
698*9a0bf528SMauro Carvalho Chehab 	/* freq		Channel carrier frequency in KHz (i.e. 1550000 KHz)
699*9a0bf528SMauro Carvalho Chehab 	 datarate	Channel symbol rate in Sps (i.e. 22500000 Sps)*/
700*9a0bf528SMauro Carvalho Chehab 
701*9a0bf528SMauro Carvalho Chehab 	/* in MHz */
702*9a0bf528SMauro Carvalho Chehab 	unsigned char coarse_tune_freq;
703*9a0bf528SMauro Carvalho Chehab 	int fine_tune_freq;
704*9a0bf528SMauro Carvalho Chehab 	unsigned char sample_rate = 0;
705*9a0bf528SMauro Carvalho Chehab 	/* boolean */
706*9a0bf528SMauro Carvalho Chehab 	bool inband_interferer_ind;
707*9a0bf528SMauro Carvalho Chehab 
708*9a0bf528SMauro Carvalho Chehab 	/* INTERMEDIATE VALUES */
709*9a0bf528SMauro Carvalho Chehab 	int icoarse_tune_freq; /* MHz */
710*9a0bf528SMauro Carvalho Chehab 	int ifine_tune_freq; /* MHz */
711*9a0bf528SMauro Carvalho Chehab 	unsigned int band_high;
712*9a0bf528SMauro Carvalho Chehab 	unsigned int band_low;
713*9a0bf528SMauro Carvalho Chehab 	unsigned int x1;
714*9a0bf528SMauro Carvalho Chehab 	unsigned int x2;
715*9a0bf528SMauro Carvalho Chehab 	int i;
716*9a0bf528SMauro Carvalho Chehab 	bool inband_interferer_div2[ALLOWABLE_FS_COUNT];
717*9a0bf528SMauro Carvalho Chehab 	bool inband_interferer_div4[ALLOWABLE_FS_COUNT];
718*9a0bf528SMauro Carvalho Chehab 	int status;
719*9a0bf528SMauro Carvalho Chehab 
720*9a0bf528SMauro Carvalho Chehab 	/* allowable sample rates for ADC in MHz */
721*9a0bf528SMauro Carvalho Chehab 	int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195,
722*9a0bf528SMauro Carvalho Chehab 					196, 204, 205, 206, 207
723*9a0bf528SMauro Carvalho Chehab 	};
724*9a0bf528SMauro Carvalho Chehab 	/* in MHz */
725*9a0bf528SMauro Carvalho Chehab 	int if_limit_high;
726*9a0bf528SMauro Carvalho Chehab 	int if_limit_low;
727*9a0bf528SMauro Carvalho Chehab 	int lnb_lo;
728*9a0bf528SMauro Carvalho Chehab 	int lnb_uncertanity;
729*9a0bf528SMauro Carvalho Chehab 
730*9a0bf528SMauro Carvalho Chehab 	int rf_freq;
731*9a0bf528SMauro Carvalho Chehab 	int data_rate;
732*9a0bf528SMauro Carvalho Chehab 	unsigned char regs[4];
733*9a0bf528SMauro Carvalho Chehab 
734*9a0bf528SMauro Carvalho Chehab 	dprintk("%s : FE_SET_FRONTEND\n", __func__);
735*9a0bf528SMauro Carvalho Chehab 
736*9a0bf528SMauro Carvalho Chehab 	if (c->delivery_system != SYS_DVBS) {
737*9a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported delivery system selected (%d)\n",
738*9a0bf528SMauro Carvalho Chehab 				__func__, c->delivery_system);
739*9a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
740*9a0bf528SMauro Carvalho Chehab 	}
741*9a0bf528SMauro Carvalho Chehab 
742*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
743*9a0bf528SMauro Carvalho Chehab 		inband_interferer_div2[i] = inband_interferer_div4[i] = false;
744*9a0bf528SMauro Carvalho Chehab 
745*9a0bf528SMauro Carvalho Chehab 	if_limit_high = -700000;
746*9a0bf528SMauro Carvalho Chehab 	if_limit_low = -100000;
747*9a0bf528SMauro Carvalho Chehab 	/* in MHz */
748*9a0bf528SMauro Carvalho Chehab 	lnb_lo = 0;
749*9a0bf528SMauro Carvalho Chehab 	lnb_uncertanity = 0;
750*9a0bf528SMauro Carvalho Chehab 
751*9a0bf528SMauro Carvalho Chehab 	rf_freq = 10 * c->frequency ;
752*9a0bf528SMauro Carvalho Chehab 	data_rate = c->symbol_rate / 100;
753*9a0bf528SMauro Carvalho Chehab 
754*9a0bf528SMauro Carvalho Chehab 	status = PASS;
755*9a0bf528SMauro Carvalho Chehab 
756*9a0bf528SMauro Carvalho Chehab 	band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200)
757*9a0bf528SMauro Carvalho Chehab 					+ (data_rate * 135)) / 200;
758*9a0bf528SMauro Carvalho Chehab 
759*9a0bf528SMauro Carvalho Chehab 	band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200)
760*9a0bf528SMauro Carvalho Chehab 					+ (data_rate * 135)) / 200;
761*9a0bf528SMauro Carvalho Chehab 
762*9a0bf528SMauro Carvalho Chehab 
763*9a0bf528SMauro Carvalho Chehab 	icoarse_tune_freq = 100000 *
764*9a0bf528SMauro Carvalho Chehab 				(((rf_freq - lnb_lo) -
765*9a0bf528SMauro Carvalho Chehab 					(if_limit_low + if_limit_high) / 2)
766*9a0bf528SMauro Carvalho Chehab 								/ 100000);
767*9a0bf528SMauro Carvalho Chehab 
768*9a0bf528SMauro Carvalho Chehab 	ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
769*9a0bf528SMauro Carvalho Chehab 
770*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
771*9a0bf528SMauro Carvalho Chehab 		x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
772*9a0bf528SMauro Carvalho Chehab 					(afs[i] * 2500) + afs[i] * 2500;
773*9a0bf528SMauro Carvalho Chehab 
774*9a0bf528SMauro Carvalho Chehab 		x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
775*9a0bf528SMauro Carvalho Chehab 							(afs[i] * 2500);
776*9a0bf528SMauro Carvalho Chehab 
777*9a0bf528SMauro Carvalho Chehab 		if (((band_low < x1) && (x1 < band_high)) ||
778*9a0bf528SMauro Carvalho Chehab 					((band_low < x2) && (x2 < band_high)))
779*9a0bf528SMauro Carvalho Chehab 					inband_interferer_div4[i] = true;
780*9a0bf528SMauro Carvalho Chehab 
781*9a0bf528SMauro Carvalho Chehab 	}
782*9a0bf528SMauro Carvalho Chehab 
783*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
784*9a0bf528SMauro Carvalho Chehab 		x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
785*9a0bf528SMauro Carvalho Chehab 					(afs[i] * 5000) + afs[i] * 5000;
786*9a0bf528SMauro Carvalho Chehab 
787*9a0bf528SMauro Carvalho Chehab 		x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
788*9a0bf528SMauro Carvalho Chehab 					(afs[i] * 5000);
789*9a0bf528SMauro Carvalho Chehab 
790*9a0bf528SMauro Carvalho Chehab 		if (((band_low < x1) && (x1 < band_high)) ||
791*9a0bf528SMauro Carvalho Chehab 					((band_low < x2) && (x2 < band_high)))
792*9a0bf528SMauro Carvalho Chehab 					inband_interferer_div2[i] = true;
793*9a0bf528SMauro Carvalho Chehab 	}
794*9a0bf528SMauro Carvalho Chehab 
795*9a0bf528SMauro Carvalho Chehab 	inband_interferer_ind = true;
796*9a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
797*9a0bf528SMauro Carvalho Chehab 		if (inband_interferer_div2[i] || inband_interferer_div4[i]) {
798*9a0bf528SMauro Carvalho Chehab 			inband_interferer_ind = false;
799*9a0bf528SMauro Carvalho Chehab 			break;
800*9a0bf528SMauro Carvalho Chehab 		}
801*9a0bf528SMauro Carvalho Chehab 	}
802*9a0bf528SMauro Carvalho Chehab 
803*9a0bf528SMauro Carvalho Chehab 	if (inband_interferer_ind) {
804*9a0bf528SMauro Carvalho Chehab 		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
805*9a0bf528SMauro Carvalho Chehab 			if (!inband_interferer_div2[i]) {
806*9a0bf528SMauro Carvalho Chehab 				sample_rate = (u8) afs[i];
807*9a0bf528SMauro Carvalho Chehab 				break;
808*9a0bf528SMauro Carvalho Chehab 			}
809*9a0bf528SMauro Carvalho Chehab 		}
810*9a0bf528SMauro Carvalho Chehab 	} else {
811*9a0bf528SMauro Carvalho Chehab 		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
812*9a0bf528SMauro Carvalho Chehab 			if ((inband_interferer_div2[i] ||
813*9a0bf528SMauro Carvalho Chehab 			     !inband_interferer_div4[i])) {
814*9a0bf528SMauro Carvalho Chehab 				sample_rate = (u8) afs[i];
815*9a0bf528SMauro Carvalho Chehab 				break;
816*9a0bf528SMauro Carvalho Chehab 			}
817*9a0bf528SMauro Carvalho Chehab 		}
818*9a0bf528SMauro Carvalho Chehab 
819*9a0bf528SMauro Carvalho Chehab 	}
820*9a0bf528SMauro Carvalho Chehab 
821*9a0bf528SMauro Carvalho Chehab 	if (sample_rate > 207 || sample_rate < 192)
822*9a0bf528SMauro Carvalho Chehab 		sample_rate = 200;
823*9a0bf528SMauro Carvalho Chehab 
824*9a0bf528SMauro Carvalho Chehab 	fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) /
825*9a0bf528SMauro Carvalho Chehab 					((sample_rate) * 1000));
826*9a0bf528SMauro Carvalho Chehab 
827*9a0bf528SMauro Carvalho Chehab 	coarse_tune_freq = (u8)(icoarse_tune_freq / 100000);
828*9a0bf528SMauro Carvalho Chehab 
829*9a0bf528SMauro Carvalho Chehab 	regs[0] = sample_rate;
830*9a0bf528SMauro Carvalho Chehab 	regs[1] = coarse_tune_freq;
831*9a0bf528SMauro Carvalho Chehab 	regs[2] = fine_tune_freq & 0xFF;
832*9a0bf528SMauro Carvalho Chehab 	regs[3] = fine_tune_freq >> 8 & 0xFF;
833*9a0bf528SMauro Carvalho Chehab 
834*9a0bf528SMauro Carvalho Chehab 	status |= si21_writeregs(state, PLL_DIVISOR_REG, &regs[0], 0x04);
835*9a0bf528SMauro Carvalho Chehab 
836*9a0bf528SMauro Carvalho Chehab 	state->fs = sample_rate;/*ADC MHz*/
837*9a0bf528SMauro Carvalho Chehab 	si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
838*9a0bf528SMauro Carvalho Chehab 
839*9a0bf528SMauro Carvalho Chehab 	return 0;
840*9a0bf528SMauro Carvalho Chehab }
841*9a0bf528SMauro Carvalho Chehab 
842*9a0bf528SMauro Carvalho Chehab static int si21xx_sleep(struct dvb_frontend *fe)
843*9a0bf528SMauro Carvalho Chehab {
844*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
845*9a0bf528SMauro Carvalho Chehab 	u8 regdata;
846*9a0bf528SMauro Carvalho Chehab 
847*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
848*9a0bf528SMauro Carvalho Chehab 
849*9a0bf528SMauro Carvalho Chehab 	si21_readregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
850*9a0bf528SMauro Carvalho Chehab 	regdata |= 1 << 6;
851*9a0bf528SMauro Carvalho Chehab 	si21_writeregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
852*9a0bf528SMauro Carvalho Chehab 	state->initialised = 0;
853*9a0bf528SMauro Carvalho Chehab 
854*9a0bf528SMauro Carvalho Chehab 	return 0;
855*9a0bf528SMauro Carvalho Chehab }
856*9a0bf528SMauro Carvalho Chehab 
857*9a0bf528SMauro Carvalho Chehab static void si21xx_release(struct dvb_frontend *fe)
858*9a0bf528SMauro Carvalho Chehab {
859*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = fe->demodulator_priv;
860*9a0bf528SMauro Carvalho Chehab 
861*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
862*9a0bf528SMauro Carvalho Chehab 
863*9a0bf528SMauro Carvalho Chehab 	kfree(state);
864*9a0bf528SMauro Carvalho Chehab }
865*9a0bf528SMauro Carvalho Chehab 
866*9a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops si21xx_ops = {
867*9a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
868*9a0bf528SMauro Carvalho Chehab 	.info = {
869*9a0bf528SMauro Carvalho Chehab 		.name			= "SL SI21XX DVB-S",
870*9a0bf528SMauro Carvalho Chehab 		.frequency_min		= 950000,
871*9a0bf528SMauro Carvalho Chehab 		.frequency_max		= 2150000,
872*9a0bf528SMauro Carvalho Chehab 		.frequency_stepsize	= 125,	 /* kHz for QPSK frontends */
873*9a0bf528SMauro Carvalho Chehab 		.frequency_tolerance	= 0,
874*9a0bf528SMauro Carvalho Chehab 		.symbol_rate_min	= 1000000,
875*9a0bf528SMauro Carvalho Chehab 		.symbol_rate_max	= 45000000,
876*9a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance	= 500,	/* ppm */
877*9a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
878*9a0bf528SMauro Carvalho Chehab 		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
879*9a0bf528SMauro Carvalho Chehab 		FE_CAN_QPSK |
880*9a0bf528SMauro Carvalho Chehab 		FE_CAN_FEC_AUTO
881*9a0bf528SMauro Carvalho Chehab 	},
882*9a0bf528SMauro Carvalho Chehab 
883*9a0bf528SMauro Carvalho Chehab 	.release = si21xx_release,
884*9a0bf528SMauro Carvalho Chehab 	.init = si21xx_init,
885*9a0bf528SMauro Carvalho Chehab 	.sleep = si21xx_sleep,
886*9a0bf528SMauro Carvalho Chehab 	.write = si21_write,
887*9a0bf528SMauro Carvalho Chehab 	.read_status = si21_read_status,
888*9a0bf528SMauro Carvalho Chehab 	.read_ber = si21_read_ber,
889*9a0bf528SMauro Carvalho Chehab 	.read_signal_strength = si21_read_signal_strength,
890*9a0bf528SMauro Carvalho Chehab 	.read_snr = si21_read_snr,
891*9a0bf528SMauro Carvalho Chehab 	.read_ucblocks = si21_read_ucblocks,
892*9a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = si21xx_send_diseqc_msg,
893*9a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = si21xx_send_diseqc_burst,
894*9a0bf528SMauro Carvalho Chehab 	.set_tone = si21xx_set_tone,
895*9a0bf528SMauro Carvalho Chehab 	.set_voltage = si21xx_set_voltage,
896*9a0bf528SMauro Carvalho Chehab 
897*9a0bf528SMauro Carvalho Chehab 	.set_frontend = si21xx_set_frontend,
898*9a0bf528SMauro Carvalho Chehab };
899*9a0bf528SMauro Carvalho Chehab 
900*9a0bf528SMauro Carvalho Chehab struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
901*9a0bf528SMauro Carvalho Chehab 						struct i2c_adapter *i2c)
902*9a0bf528SMauro Carvalho Chehab {
903*9a0bf528SMauro Carvalho Chehab 	struct si21xx_state *state = NULL;
904*9a0bf528SMauro Carvalho Chehab 	int id;
905*9a0bf528SMauro Carvalho Chehab 
906*9a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
907*9a0bf528SMauro Carvalho Chehab 
908*9a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
909*9a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL);
910*9a0bf528SMauro Carvalho Chehab 	if (state == NULL)
911*9a0bf528SMauro Carvalho Chehab 		goto error;
912*9a0bf528SMauro Carvalho Chehab 
913*9a0bf528SMauro Carvalho Chehab 	/* setup the state */
914*9a0bf528SMauro Carvalho Chehab 	state->config = config;
915*9a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
916*9a0bf528SMauro Carvalho Chehab 	state->initialised = 0;
917*9a0bf528SMauro Carvalho Chehab 	state->errmode = STATUS_BER;
918*9a0bf528SMauro Carvalho Chehab 
919*9a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
920*9a0bf528SMauro Carvalho Chehab 	id = si21_readreg(state, SYSTEM_MODE_REG);
921*9a0bf528SMauro Carvalho Chehab 	si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */
922*9a0bf528SMauro Carvalho Chehab 	msleep(200);
923*9a0bf528SMauro Carvalho Chehab 	id = si21_readreg(state, 0x00);
924*9a0bf528SMauro Carvalho Chehab 
925*9a0bf528SMauro Carvalho Chehab 	/* register 0x00 contains:
926*9a0bf528SMauro Carvalho Chehab 		0x34 for SI2107
927*9a0bf528SMauro Carvalho Chehab 		0x24 for SI2108
928*9a0bf528SMauro Carvalho Chehab 		0x14 for SI2109
929*9a0bf528SMauro Carvalho Chehab 		0x04 for SI2110
930*9a0bf528SMauro Carvalho Chehab 	*/
931*9a0bf528SMauro Carvalho Chehab 	if (id != 0x04 && id != 0x14)
932*9a0bf528SMauro Carvalho Chehab 		goto error;
933*9a0bf528SMauro Carvalho Chehab 
934*9a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
935*9a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &si21xx_ops,
936*9a0bf528SMauro Carvalho Chehab 					sizeof(struct dvb_frontend_ops));
937*9a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
938*9a0bf528SMauro Carvalho Chehab 	return &state->frontend;
939*9a0bf528SMauro Carvalho Chehab 
940*9a0bf528SMauro Carvalho Chehab error:
941*9a0bf528SMauro Carvalho Chehab 	kfree(state);
942*9a0bf528SMauro Carvalho Chehab 	return NULL;
943*9a0bf528SMauro Carvalho Chehab }
944*9a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(si21xx_attach);
945*9a0bf528SMauro Carvalho Chehab 
946*9a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
947*9a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
948*9a0bf528SMauro Carvalho Chehab 
949*9a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver");
950*9a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Igor M. Liplianin");
951*9a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
952