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