xref: /OK3568_Linux_fs/kernel/drivers/media/dvb-frontends/lgs8gl5.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun     Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun     Copyright (C) 2008 Sirius International (Hong Kong) Limited
6*4882a593Smuzhiyun 	Timothy Lee <timothy.lee@siriushk.com>
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 "lgs8gl5.h"
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define REG_RESET		0x02
21*4882a593Smuzhiyun #define REG_RESET_OFF			0x01
22*4882a593Smuzhiyun #define REG_03			0x03
23*4882a593Smuzhiyun #define REG_04			0x04
24*4882a593Smuzhiyun #define REG_07			0x07
25*4882a593Smuzhiyun #define REG_09			0x09
26*4882a593Smuzhiyun #define REG_0A			0x0a
27*4882a593Smuzhiyun #define REG_0B			0x0b
28*4882a593Smuzhiyun #define REG_0C			0x0c
29*4882a593Smuzhiyun #define REG_37			0x37
30*4882a593Smuzhiyun #define REG_STRENGTH		0x4b
31*4882a593Smuzhiyun #define REG_STRENGTH_MASK		0x7f
32*4882a593Smuzhiyun #define REG_STRENGTH_CARRIER		0x80
33*4882a593Smuzhiyun #define REG_INVERSION		0x7c
34*4882a593Smuzhiyun #define REG_INVERSION_ON		0x80
35*4882a593Smuzhiyun #define REG_7D			0x7d
36*4882a593Smuzhiyun #define REG_7E			0x7e
37*4882a593Smuzhiyun #define REG_A2			0xa2
38*4882a593Smuzhiyun #define REG_STATUS		0xa4
39*4882a593Smuzhiyun #define REG_STATUS_SYNC		0x04
40*4882a593Smuzhiyun #define REG_STATUS_LOCK		0x01
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun struct lgs8gl5_state {
44*4882a593Smuzhiyun 	struct i2c_adapter *i2c;
45*4882a593Smuzhiyun 	const struct lgs8gl5_config *config;
46*4882a593Smuzhiyun 	struct dvb_frontend frontend;
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static int debug;
51*4882a593Smuzhiyun #define dprintk(args...) \
52*4882a593Smuzhiyun 	do { \
53*4882a593Smuzhiyun 		if (debug) \
54*4882a593Smuzhiyun 			printk(KERN_DEBUG "lgs8gl5: " args); \
55*4882a593Smuzhiyun 	} while (0)
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /* Writes into demod's register */
59*4882a593Smuzhiyun static int
lgs8gl5_write_reg(struct lgs8gl5_state * state,u8 reg,u8 data)60*4882a593Smuzhiyun lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	int ret;
63*4882a593Smuzhiyun 	u8 buf[] = {reg, data};
64*4882a593Smuzhiyun 	struct i2c_msg msg = {
65*4882a593Smuzhiyun 		.addr  = state->config->demod_address,
66*4882a593Smuzhiyun 		.flags = 0,
67*4882a593Smuzhiyun 		.buf   = buf,
68*4882a593Smuzhiyun 		.len   = 2
69*4882a593Smuzhiyun 	};
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, &msg, 1);
72*4882a593Smuzhiyun 	if (ret != 1)
73*4882a593Smuzhiyun 		dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
74*4882a593Smuzhiyun 			__func__, reg, data, ret);
75*4882a593Smuzhiyun 	return (ret != 1) ? -1 : 0;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* Reads from demod's register */
80*4882a593Smuzhiyun static int
lgs8gl5_read_reg(struct lgs8gl5_state * state,u8 reg)81*4882a593Smuzhiyun lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	int ret;
84*4882a593Smuzhiyun 	u8 b0[] = {reg};
85*4882a593Smuzhiyun 	u8 b1[] = {0};
86*4882a593Smuzhiyun 	struct i2c_msg msg[2] = {
87*4882a593Smuzhiyun 		{
88*4882a593Smuzhiyun 			.addr  = state->config->demod_address,
89*4882a593Smuzhiyun 			.flags = 0,
90*4882a593Smuzhiyun 			.buf   = b0,
91*4882a593Smuzhiyun 			.len   = 1
92*4882a593Smuzhiyun 		},
93*4882a593Smuzhiyun 		{
94*4882a593Smuzhiyun 			.addr  = state->config->demod_address,
95*4882a593Smuzhiyun 			.flags = I2C_M_RD,
96*4882a593Smuzhiyun 			.buf   = b1,
97*4882a593Smuzhiyun 			.len   = 1
98*4882a593Smuzhiyun 		}
99*4882a593Smuzhiyun 	};
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, msg, 2);
102*4882a593Smuzhiyun 	if (ret != 2)
103*4882a593Smuzhiyun 		return -EIO;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	return b1[0];
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun static int
lgs8gl5_update_reg(struct lgs8gl5_state * state,u8 reg,u8 data)110*4882a593Smuzhiyun lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	lgs8gl5_read_reg(state, reg);
113*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, reg, data);
114*4882a593Smuzhiyun 	return 0;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun /* Writes into alternate device's register */
119*4882a593Smuzhiyun /* TODO:  Find out what that device is for! */
120*4882a593Smuzhiyun static int
lgs8gl5_update_alt_reg(struct lgs8gl5_state * state,u8 reg,u8 data)121*4882a593Smuzhiyun lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	int ret;
124*4882a593Smuzhiyun 	u8 b0[] = {reg};
125*4882a593Smuzhiyun 	u8 b1[] = {0};
126*4882a593Smuzhiyun 	u8 b2[] = {reg, data};
127*4882a593Smuzhiyun 	struct i2c_msg msg[3] = {
128*4882a593Smuzhiyun 		{
129*4882a593Smuzhiyun 			.addr  = state->config->demod_address + 2,
130*4882a593Smuzhiyun 			.flags = 0,
131*4882a593Smuzhiyun 			.buf   = b0,
132*4882a593Smuzhiyun 			.len   = 1
133*4882a593Smuzhiyun 		},
134*4882a593Smuzhiyun 		{
135*4882a593Smuzhiyun 			.addr  = state->config->demod_address + 2,
136*4882a593Smuzhiyun 			.flags = I2C_M_RD,
137*4882a593Smuzhiyun 			.buf   = b1,
138*4882a593Smuzhiyun 			.len   = 1
139*4882a593Smuzhiyun 		},
140*4882a593Smuzhiyun 		{
141*4882a593Smuzhiyun 			.addr  = state->config->demod_address + 2,
142*4882a593Smuzhiyun 			.flags = 0,
143*4882a593Smuzhiyun 			.buf   = b2,
144*4882a593Smuzhiyun 			.len   = 2
145*4882a593Smuzhiyun 		},
146*4882a593Smuzhiyun 	};
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, msg, 3);
149*4882a593Smuzhiyun 	return (ret != 3) ? -1 : 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun static void
lgs8gl5_soft_reset(struct lgs8gl5_state * state)154*4882a593Smuzhiyun lgs8gl5_soft_reset(struct lgs8gl5_state *state)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	u8 val;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	val = lgs8gl5_read_reg(state, REG_RESET);
161*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
162*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
163*4882a593Smuzhiyun 	msleep(5);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun /* Starts demodulation */
168*4882a593Smuzhiyun static void
lgs8gl5_start_demod(struct lgs8gl5_state * state)169*4882a593Smuzhiyun lgs8gl5_start_demod(struct lgs8gl5_state *state)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	u8  val;
172*4882a593Smuzhiyun 	int n;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
177*4882a593Smuzhiyun 	lgs8gl5_soft_reset(state);
178*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_07, 0x10);
179*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_07, 0x10);
180*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_09, 0x0e);
181*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0A, 0xe5);
182*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0B, 0x35);
183*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0C, 0x30);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_03, 0x00);
186*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_7E, 0x01);
187*4882a593Smuzhiyun 	lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
188*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_04, 0x02);
189*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_37, 0x01);
190*4882a593Smuzhiyun 	lgs8gl5_soft_reset(state);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	/* Wait for carrier */
193*4882a593Smuzhiyun 	for (n = 0;  n < 10;  n++) {
194*4882a593Smuzhiyun 		val = lgs8gl5_read_reg(state, REG_STRENGTH);
195*4882a593Smuzhiyun 		dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
196*4882a593Smuzhiyun 		if (val & REG_STRENGTH_CARRIER)
197*4882a593Smuzhiyun 			break;
198*4882a593Smuzhiyun 		msleep(4);
199*4882a593Smuzhiyun 	}
200*4882a593Smuzhiyun 	if (!(val & REG_STRENGTH_CARRIER))
201*4882a593Smuzhiyun 		return;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	/* Wait for lock */
204*4882a593Smuzhiyun 	for (n = 0;  n < 20;  n++) {
205*4882a593Smuzhiyun 		val = lgs8gl5_read_reg(state, REG_STATUS);
206*4882a593Smuzhiyun 		dprintk("Wait for lock[%d] 0x%02X\n", n, val);
207*4882a593Smuzhiyun 		if (val & REG_STATUS_LOCK)
208*4882a593Smuzhiyun 			break;
209*4882a593Smuzhiyun 		msleep(12);
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 	if (!(val & REG_STATUS_LOCK))
212*4882a593Smuzhiyun 		return;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
215*4882a593Smuzhiyun 	lgs8gl5_soft_reset(state);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun static int
lgs8gl5_init(struct dvb_frontend * fe)220*4882a593Smuzhiyun lgs8gl5_init(struct dvb_frontend *fe)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
227*4882a593Smuzhiyun 	lgs8gl5_soft_reset(state);
228*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_07, 0x10);
229*4882a593Smuzhiyun 	lgs8gl5_update_reg(state, REG_07, 0x10);
230*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_09, 0x0e);
231*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0A, 0xe5);
232*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0B, 0x35);
233*4882a593Smuzhiyun 	lgs8gl5_write_reg(state, REG_0C, 0x30);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	return 0;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun static int
lgs8gl5_read_status(struct dvb_frontend * fe,enum fe_status * status)240*4882a593Smuzhiyun lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
243*4882a593Smuzhiyun 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
244*4882a593Smuzhiyun 	u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	*status = 0;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	if ((level & REG_STRENGTH_MASK) > 0)
249*4882a593Smuzhiyun 		*status |= FE_HAS_SIGNAL;
250*4882a593Smuzhiyun 	if (level & REG_STRENGTH_CARRIER)
251*4882a593Smuzhiyun 		*status |= FE_HAS_CARRIER;
252*4882a593Smuzhiyun 	if (flags & REG_STATUS_SYNC)
253*4882a593Smuzhiyun 		*status |= FE_HAS_SYNC;
254*4882a593Smuzhiyun 	if (flags & REG_STATUS_LOCK)
255*4882a593Smuzhiyun 		*status |= FE_HAS_LOCK;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	return 0;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun static int
lgs8gl5_read_ber(struct dvb_frontend * fe,u32 * ber)262*4882a593Smuzhiyun lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	*ber = 0;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun static int
lgs8gl5_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)271*4882a593Smuzhiyun lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
274*4882a593Smuzhiyun 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
275*4882a593Smuzhiyun 	*signal_strength = (level & REG_STRENGTH_MASK) << 8;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	return 0;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun static int
lgs8gl5_read_snr(struct dvb_frontend * fe,u16 * snr)282*4882a593Smuzhiyun lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
285*4882a593Smuzhiyun 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
286*4882a593Smuzhiyun 	*snr = (level & REG_STRENGTH_MASK) << 8;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	return 0;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun static int
lgs8gl5_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)293*4882a593Smuzhiyun lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun 	*ucblocks = 0;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	return 0;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun static int
lgs8gl5_set_frontend(struct dvb_frontend * fe)302*4882a593Smuzhiyun lgs8gl5_set_frontend(struct dvb_frontend *fe)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
305*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	if (p->bandwidth_hz != 8000000)
310*4882a593Smuzhiyun 		return -EINVAL;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	if (fe->ops.tuner_ops.set_params) {
313*4882a593Smuzhiyun 		fe->ops.tuner_ops.set_params(fe);
314*4882a593Smuzhiyun 		if (fe->ops.i2c_gate_ctrl)
315*4882a593Smuzhiyun 			fe->ops.i2c_gate_ctrl(fe, 0);
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	/* lgs8gl5_set_inversion(state, p->inversion); */
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	lgs8gl5_start_demod(state);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun static int
lgs8gl5_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * p)327*4882a593Smuzhiyun lgs8gl5_get_frontend(struct dvb_frontend *fe,
328*4882a593Smuzhiyun 		     struct dtv_frontend_properties *p)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	p->code_rate_HP = FEC_1_2;
337*4882a593Smuzhiyun 	p->code_rate_LP = FEC_7_8;
338*4882a593Smuzhiyun 	p->guard_interval = GUARD_INTERVAL_1_32;
339*4882a593Smuzhiyun 	p->transmission_mode = TRANSMISSION_MODE_2K;
340*4882a593Smuzhiyun 	p->modulation = QAM_64;
341*4882a593Smuzhiyun 	p->hierarchy = HIERARCHY_NONE;
342*4882a593Smuzhiyun 	p->bandwidth_hz = 8000000;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	return 0;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun static int
lgs8gl5_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * fesettings)349*4882a593Smuzhiyun lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
350*4882a593Smuzhiyun 		struct dvb_frontend_tune_settings *fesettings)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun 	fesettings->min_delay_ms = 240;
353*4882a593Smuzhiyun 	fesettings->step_size    = 0;
354*4882a593Smuzhiyun 	fesettings->max_drift    = 0;
355*4882a593Smuzhiyun 	return 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun static void
lgs8gl5_release(struct dvb_frontend * fe)360*4882a593Smuzhiyun lgs8gl5_release(struct dvb_frontend *fe)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	struct lgs8gl5_state *state = fe->demodulator_priv;
363*4882a593Smuzhiyun 	kfree(state);
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun static const struct dvb_frontend_ops lgs8gl5_ops;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun struct dvb_frontend*
lgs8gl5_attach(const struct lgs8gl5_config * config,struct i2c_adapter * i2c)371*4882a593Smuzhiyun lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	struct lgs8gl5_state *state = NULL;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	/* Allocate memory for the internal state */
378*4882a593Smuzhiyun 	state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
379*4882a593Smuzhiyun 	if (state == NULL)
380*4882a593Smuzhiyun 		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 (lgs8gl5_read_reg(state, REG_RESET) < 0)
388*4882a593Smuzhiyun 		goto error;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	/* Create dvb_frontend */
391*4882a593Smuzhiyun 	memcpy(&state->frontend.ops, &lgs8gl5_ops,
392*4882a593Smuzhiyun 		sizeof(struct dvb_frontend_ops));
393*4882a593Smuzhiyun 	state->frontend.demodulator_priv = state;
394*4882a593Smuzhiyun 	return &state->frontend;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun error:
397*4882a593Smuzhiyun 	kfree(state);
398*4882a593Smuzhiyun 	return NULL;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun EXPORT_SYMBOL(lgs8gl5_attach);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun static const struct dvb_frontend_ops lgs8gl5_ops = {
404*4882a593Smuzhiyun 	.delsys = { SYS_DTMB },
405*4882a593Smuzhiyun 	.info = {
406*4882a593Smuzhiyun 		.name			= "Legend Silicon LGS-8GL5 DMB-TH",
407*4882a593Smuzhiyun 		.frequency_min_hz	= 474 * MHz,
408*4882a593Smuzhiyun 		.frequency_max_hz	= 858 * MHz,
409*4882a593Smuzhiyun 		.frequency_stepsize_hz	=  10 * kHz,
410*4882a593Smuzhiyun 		.caps = FE_CAN_FEC_AUTO |
411*4882a593Smuzhiyun 			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
412*4882a593Smuzhiyun 			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
413*4882a593Smuzhiyun 			FE_CAN_TRANSMISSION_MODE_AUTO |
414*4882a593Smuzhiyun 			FE_CAN_BANDWIDTH_AUTO |
415*4882a593Smuzhiyun 			FE_CAN_GUARD_INTERVAL_AUTO |
416*4882a593Smuzhiyun 			FE_CAN_HIERARCHY_AUTO |
417*4882a593Smuzhiyun 			FE_CAN_RECOVER
418*4882a593Smuzhiyun 	},
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	.release = lgs8gl5_release,
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	.init = lgs8gl5_init,
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	.set_frontend = lgs8gl5_set_frontend,
425*4882a593Smuzhiyun 	.get_frontend = lgs8gl5_get_frontend,
426*4882a593Smuzhiyun 	.get_tune_settings = lgs8gl5_get_tune_settings,
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	.read_status = lgs8gl5_read_status,
429*4882a593Smuzhiyun 	.read_ber = lgs8gl5_read_ber,
430*4882a593Smuzhiyun 	.read_signal_strength = lgs8gl5_read_signal_strength,
431*4882a593Smuzhiyun 	.read_snr = lgs8gl5_read_snr,
432*4882a593Smuzhiyun 	.read_ucblocks = lgs8gl5_read_ucblocks,
433*4882a593Smuzhiyun };
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun module_param(debug, int, 0644);
437*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
440*4882a593Smuzhiyun MODULE_AUTHOR("Timothy Lee");
441*4882a593Smuzhiyun MODULE_LICENSE("GPL");
442