xref: /OK3568_Linux_fs/kernel/drivers/media/dvb-frontends/cx22700.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun     Conexant cx22700 DVB OFDM demodulator driver
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun     Copyright (C) 2001-2002 Convergence Integrated Media GmbH
6*4882a593Smuzhiyun 	Holger Waechtler <holger@convergence.de>
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/string.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <media/dvb_frontend.h>
17*4882a593Smuzhiyun #include "cx22700.h"
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun struct cx22700_state {
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	struct i2c_adapter* i2c;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun 	const struct cx22700_config* config;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	struct dvb_frontend frontend;
27*4882a593Smuzhiyun };
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static int debug;
31*4882a593Smuzhiyun #define dprintk(args...) \
32*4882a593Smuzhiyun 	do { \
33*4882a593Smuzhiyun 		if (debug) printk(KERN_DEBUG "cx22700: " args); \
34*4882a593Smuzhiyun 	} while (0)
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun static u8 init_tab [] = {
37*4882a593Smuzhiyun 	0x04, 0x10,
38*4882a593Smuzhiyun 	0x05, 0x09,
39*4882a593Smuzhiyun 	0x06, 0x00,
40*4882a593Smuzhiyun 	0x08, 0x04,
41*4882a593Smuzhiyun 	0x09, 0x00,
42*4882a593Smuzhiyun 	0x0a, 0x01,
43*4882a593Smuzhiyun 	0x15, 0x40,
44*4882a593Smuzhiyun 	0x16, 0x10,
45*4882a593Smuzhiyun 	0x17, 0x87,
46*4882a593Smuzhiyun 	0x18, 0x17,
47*4882a593Smuzhiyun 	0x1a, 0x10,
48*4882a593Smuzhiyun 	0x25, 0x04,
49*4882a593Smuzhiyun 	0x2e, 0x00,
50*4882a593Smuzhiyun 	0x39, 0x00,
51*4882a593Smuzhiyun 	0x3a, 0x04,
52*4882a593Smuzhiyun 	0x45, 0x08,
53*4882a593Smuzhiyun 	0x46, 0x02,
54*4882a593Smuzhiyun 	0x47, 0x05,
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 
cx22700_writereg(struct cx22700_state * state,u8 reg,u8 data)58*4882a593Smuzhiyun static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	int ret;
61*4882a593Smuzhiyun 	u8 buf [] = { reg, data };
62*4882a593Smuzhiyun 	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	dprintk ("%s\n", __func__);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	ret = i2c_transfer (state->i2c, &msg, 1);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (ret != 1)
69*4882a593Smuzhiyun 		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
70*4882a593Smuzhiyun 			__func__, reg, data, ret);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	return (ret != 1) ? -1 : 0;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
cx22700_readreg(struct cx22700_state * state,u8 reg)75*4882a593Smuzhiyun static int cx22700_readreg (struct cx22700_state* state, u8 reg)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	int ret;
78*4882a593Smuzhiyun 	u8 b0 [] = { reg };
79*4882a593Smuzhiyun 	u8 b1 [] = { 0 };
80*4882a593Smuzhiyun 	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
81*4882a593Smuzhiyun 			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	dprintk ("%s\n", __func__);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	ret = i2c_transfer (state->i2c, msg, 2);
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	if (ret != 2) return -EIO;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return b1[0];
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
cx22700_set_inversion(struct cx22700_state * state,int inversion)92*4882a593Smuzhiyun static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	u8 val;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	dprintk ("%s\n", __func__);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	switch (inversion) {
99*4882a593Smuzhiyun 	case INVERSION_AUTO:
100*4882a593Smuzhiyun 		return -EOPNOTSUPP;
101*4882a593Smuzhiyun 	case INVERSION_ON:
102*4882a593Smuzhiyun 		val = cx22700_readreg (state, 0x09);
103*4882a593Smuzhiyun 		return cx22700_writereg (state, 0x09, val | 0x01);
104*4882a593Smuzhiyun 	case INVERSION_OFF:
105*4882a593Smuzhiyun 		val = cx22700_readreg (state, 0x09);
106*4882a593Smuzhiyun 		return cx22700_writereg (state, 0x09, val & 0xfe);
107*4882a593Smuzhiyun 	default:
108*4882a593Smuzhiyun 		return -EINVAL;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
cx22700_set_tps(struct cx22700_state * state,struct dtv_frontend_properties * p)112*4882a593Smuzhiyun static int cx22700_set_tps(struct cx22700_state *state,
113*4882a593Smuzhiyun 			   struct dtv_frontend_properties *p)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	static const u8 qam_tab [4] = { 0, 1, 0, 2 };
116*4882a593Smuzhiyun 	static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
117*4882a593Smuzhiyun 	u8 val;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	dprintk ("%s\n", __func__);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
122*4882a593Smuzhiyun 		return -EINVAL;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
125*4882a593Smuzhiyun 		return -EINVAL;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
128*4882a593Smuzhiyun 		return -EINVAL;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
131*4882a593Smuzhiyun 	    p->guard_interval > GUARD_INTERVAL_1_4)
132*4882a593Smuzhiyun 		return -EINVAL;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
135*4882a593Smuzhiyun 	    p->transmission_mode != TRANSMISSION_MODE_8K)
136*4882a593Smuzhiyun 		return -EINVAL;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (p->modulation != QPSK &&
139*4882a593Smuzhiyun 	    p->modulation != QAM_16 &&
140*4882a593Smuzhiyun 	    p->modulation != QAM_64)
141*4882a593Smuzhiyun 		return -EINVAL;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if ((int)p->hierarchy < HIERARCHY_NONE ||
144*4882a593Smuzhiyun 	    p->hierarchy > HIERARCHY_4)
145*4882a593Smuzhiyun 		return -EINVAL;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000)
148*4882a593Smuzhiyun 		return -EINVAL;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	if (p->bandwidth_hz == 7000000)
151*4882a593Smuzhiyun 		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
152*4882a593Smuzhiyun 	else
153*4882a593Smuzhiyun 		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	val = qam_tab[p->modulation - QPSK];
156*4882a593Smuzhiyun 	val |= p->hierarchy - HIERARCHY_NONE;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	cx22700_writereg (state, 0x04, val);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) ||
161*4882a593Smuzhiyun 	    p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab))
162*4882a593Smuzhiyun 		return -EINVAL;
163*4882a593Smuzhiyun 	val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
164*4882a593Smuzhiyun 	val |= fec_tab[p->code_rate_LP - FEC_1_2];
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	cx22700_writereg (state, 0x05, val);
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
169*4882a593Smuzhiyun 	val |= p->transmission_mode - TRANSMISSION_MODE_2K;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	cx22700_writereg (state, 0x06, val);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	cx22700_writereg (state, 0x08, 0x04 | 0x02);  /* use user tps parameters */
174*4882a593Smuzhiyun 	cx22700_writereg (state, 0x08, 0x04);         /* restart acquisition */
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	return 0;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
cx22700_get_tps(struct cx22700_state * state,struct dtv_frontend_properties * p)179*4882a593Smuzhiyun static int cx22700_get_tps(struct cx22700_state *state,
180*4882a593Smuzhiyun 			   struct dtv_frontend_properties *p)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	static const enum fe_modulation qam_tab[3] = { QPSK, QAM_16, QAM_64 };
183*4882a593Smuzhiyun 	static const enum fe_code_rate fec_tab[5] = {
184*4882a593Smuzhiyun 		FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8
185*4882a593Smuzhiyun 	};
186*4882a593Smuzhiyun 	u8 val;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	dprintk ("%s\n", __func__);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	if (!(cx22700_readreg(state, 0x07) & 0x20))  /*  tps valid? */
191*4882a593Smuzhiyun 		return -EAGAIN;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	val = cx22700_readreg (state, 0x01);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if ((val & 0x7) > 4)
196*4882a593Smuzhiyun 		p->hierarchy = HIERARCHY_AUTO;
197*4882a593Smuzhiyun 	else
198*4882a593Smuzhiyun 		p->hierarchy = HIERARCHY_NONE + (val & 0x7);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	if (((val >> 3) & 0x3) > 2)
201*4882a593Smuzhiyun 		p->modulation = QAM_AUTO;
202*4882a593Smuzhiyun 	else
203*4882a593Smuzhiyun 		p->modulation = qam_tab[(val >> 3) & 0x3];
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	val = cx22700_readreg (state, 0x02);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	if (((val >> 3) & 0x07) > 4)
208*4882a593Smuzhiyun 		p->code_rate_HP = FEC_AUTO;
209*4882a593Smuzhiyun 	else
210*4882a593Smuzhiyun 		p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if ((val & 0x07) > 4)
213*4882a593Smuzhiyun 		p->code_rate_LP = FEC_AUTO;
214*4882a593Smuzhiyun 	else
215*4882a593Smuzhiyun 		p->code_rate_LP = fec_tab[val & 0x07];
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	val = cx22700_readreg (state, 0x03);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
220*4882a593Smuzhiyun 	p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	return 0;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun 
cx22700_init(struct dvb_frontend * fe)225*4882a593Smuzhiyun static int cx22700_init (struct dvb_frontend* fe)
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun {	struct cx22700_state* state = fe->demodulator_priv;
228*4882a593Smuzhiyun 	int i;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	dprintk("cx22700_init: init chip\n");
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x02);   /*  soft reset */
233*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x00);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	msleep(10);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	for (i=0; i<sizeof(init_tab); i+=2)
238*4882a593Smuzhiyun 		cx22700_writereg (state, init_tab[i], init_tab[i+1]);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x01);
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	return 0;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
cx22700_read_status(struct dvb_frontend * fe,enum fe_status * status)245*4882a593Smuzhiyun static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
250*4882a593Smuzhiyun 		   | (cx22700_readreg (state, 0x0e) << 1);
251*4882a593Smuzhiyun 	u8 sync = cx22700_readreg (state, 0x07);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	*status = 0;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	if (rs_ber < 0xff00)
256*4882a593Smuzhiyun 		*status |= FE_HAS_SIGNAL;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (sync & 0x20)
259*4882a593Smuzhiyun 		*status |= FE_HAS_CARRIER;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	if (sync & 0x10)
262*4882a593Smuzhiyun 		*status |= FE_HAS_VITERBI;
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	if (sync & 0x10)
265*4882a593Smuzhiyun 		*status |= FE_HAS_SYNC;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	if (*status == 0x0f)
268*4882a593Smuzhiyun 		*status |= FE_HAS_LOCK;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
cx22700_read_ber(struct dvb_frontend * fe,u32 * ber)273*4882a593Smuzhiyun static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	*ber = cx22700_readreg (state, 0x0c) & 0x7f;
278*4882a593Smuzhiyun 	cx22700_writereg (state, 0x0c, 0x00);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	return 0;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun 
cx22700_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)283*4882a593Smuzhiyun static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
288*4882a593Smuzhiyun 		   | (cx22700_readreg (state, 0x0e) << 1);
289*4882a593Smuzhiyun 	*signal_strength = ~rs_ber;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	return 0;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
cx22700_read_snr(struct dvb_frontend * fe,u16 * snr)294*4882a593Smuzhiyun static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
299*4882a593Smuzhiyun 		   | (cx22700_readreg (state, 0x0e) << 1);
300*4882a593Smuzhiyun 	*snr = ~rs_ber;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	return 0;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun 
cx22700_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)305*4882a593Smuzhiyun static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	*ucblocks = cx22700_readreg (state, 0x0f);
310*4882a593Smuzhiyun 	cx22700_writereg (state, 0x0f, 0x00);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	return 0;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
cx22700_set_frontend(struct dvb_frontend * fe)315*4882a593Smuzhiyun static int cx22700_set_frontend(struct dvb_frontend *fe)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
318*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
321*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x00);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (fe->ops.tuner_ops.set_params) {
324*4882a593Smuzhiyun 		fe->ops.tuner_ops.set_params(fe);
325*4882a593Smuzhiyun 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
326*4882a593Smuzhiyun 	}
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	cx22700_set_inversion(state, c->inversion);
329*4882a593Smuzhiyun 	cx22700_set_tps(state, c);
330*4882a593Smuzhiyun 	cx22700_writereg (state, 0x37, 0x01);  /* PAL loop filter off */
331*4882a593Smuzhiyun 	cx22700_writereg (state, 0x00, 0x01);  /* restart acquire */
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	return 0;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
cx22700_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)336*4882a593Smuzhiyun static int cx22700_get_frontend(struct dvb_frontend *fe,
337*4882a593Smuzhiyun 				struct dtv_frontend_properties *c)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
340*4882a593Smuzhiyun 	u8 reg09 = cx22700_readreg (state, 0x09);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
343*4882a593Smuzhiyun 	return cx22700_get_tps(state, c);
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun 
cx22700_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)346*4882a593Smuzhiyun static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	if (enable) {
351*4882a593Smuzhiyun 		return cx22700_writereg(state, 0x0a, 0x00);
352*4882a593Smuzhiyun 	} else {
353*4882a593Smuzhiyun 		return cx22700_writereg(state, 0x0a, 0x01);
354*4882a593Smuzhiyun 	}
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
cx22700_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * fesettings)357*4882a593Smuzhiyun static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	fesettings->min_delay_ms = 150;
360*4882a593Smuzhiyun 	fesettings->step_size = 166667;
361*4882a593Smuzhiyun 	fesettings->max_drift = 166667*2;
362*4882a593Smuzhiyun 	return 0;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
cx22700_release(struct dvb_frontend * fe)365*4882a593Smuzhiyun static void cx22700_release(struct dvb_frontend* fe)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	struct cx22700_state* state = fe->demodulator_priv;
368*4882a593Smuzhiyun 	kfree(state);
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun static const struct dvb_frontend_ops cx22700_ops;
372*4882a593Smuzhiyun 
cx22700_attach(const struct cx22700_config * config,struct i2c_adapter * i2c)373*4882a593Smuzhiyun struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
374*4882a593Smuzhiyun 				    struct i2c_adapter* i2c)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	struct cx22700_state* state = NULL;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	/* allocate memory for the internal state */
379*4882a593Smuzhiyun 	state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL);
380*4882a593Smuzhiyun 	if (state == NULL) goto error;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	/* setup the state */
383*4882a593Smuzhiyun 	state->config = config;
384*4882a593Smuzhiyun 	state->i2c = i2c;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	/* check if the demod is there */
387*4882a593Smuzhiyun 	if (cx22700_readreg(state, 0x07) < 0) goto error;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	/* create dvb_frontend */
390*4882a593Smuzhiyun 	memcpy(&state->frontend.ops, &cx22700_ops, sizeof(struct dvb_frontend_ops));
391*4882a593Smuzhiyun 	state->frontend.demodulator_priv = state;
392*4882a593Smuzhiyun 	return &state->frontend;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun error:
395*4882a593Smuzhiyun 	kfree(state);
396*4882a593Smuzhiyun 	return NULL;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun static const struct dvb_frontend_ops cx22700_ops = {
400*4882a593Smuzhiyun 	.delsys = { SYS_DVBT },
401*4882a593Smuzhiyun 	.info = {
402*4882a593Smuzhiyun 		.name			= "Conexant CX22700 DVB-T",
403*4882a593Smuzhiyun 		.frequency_min_hz	= 470 * MHz,
404*4882a593Smuzhiyun 		.frequency_max_hz	= 860 * MHz,
405*4882a593Smuzhiyun 		.frequency_stepsize_hz	= 166667,
406*4882a593Smuzhiyun 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
407*4882a593Smuzhiyun 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
408*4882a593Smuzhiyun 		      FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
409*4882a593Smuzhiyun 		      FE_CAN_RECOVER
410*4882a593Smuzhiyun 	},
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	.release = cx22700_release,
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	.init = cx22700_init,
415*4882a593Smuzhiyun 	.i2c_gate_ctrl = cx22700_i2c_gate_ctrl,
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	.set_frontend = cx22700_set_frontend,
418*4882a593Smuzhiyun 	.get_frontend = cx22700_get_frontend,
419*4882a593Smuzhiyun 	.get_tune_settings = cx22700_get_tune_settings,
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	.read_status = cx22700_read_status,
422*4882a593Smuzhiyun 	.read_ber = cx22700_read_ber,
423*4882a593Smuzhiyun 	.read_signal_strength = cx22700_read_signal_strength,
424*4882a593Smuzhiyun 	.read_snr = cx22700_read_snr,
425*4882a593Smuzhiyun 	.read_ucblocks = cx22700_read_ucblocks,
426*4882a593Smuzhiyun };
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun module_param(debug, int, 0644);
429*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
432*4882a593Smuzhiyun MODULE_AUTHOR("Holger Waechtler");
433*4882a593Smuzhiyun MODULE_LICENSE("GPL");
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun EXPORT_SYMBOL(cx22700_attach);
436