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