xref: /OK3568_Linux_fs/kernel/drivers/media/dvb-frontends/lgs8gxx.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *    Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
4*4882a593Smuzhiyun  *    LGS8913, LGS8GL5, LGS8G75
5*4882a593Smuzhiyun  *    experimental support LGS8G42, LGS8G52
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *    Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
8*4882a593Smuzhiyun  *    Copyright (C) 2008 Sirius International (Hong Kong) Limited
9*4882a593Smuzhiyun  *    Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <asm/div64.h>
13*4882a593Smuzhiyun #include <linux/firmware.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <media/dvb_frontend.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include "lgs8gxx.h"
18*4882a593Smuzhiyun #include "lgs8gxx_priv.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define dprintk(args...) \
21*4882a593Smuzhiyun 	do { \
22*4882a593Smuzhiyun 		if (debug) \
23*4882a593Smuzhiyun 			printk(KERN_DEBUG "lgs8gxx: " args); \
24*4882a593Smuzhiyun 	} while (0)
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun static int debug;
27*4882a593Smuzhiyun static int fake_signal_str = 1;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define LGS8GXX_FIRMWARE "lgs8g75.fw"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun module_param(debug, int, 0644);
32*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun module_param(fake_signal_str, int, 0644);
35*4882a593Smuzhiyun MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
36*4882a593Smuzhiyun "Signal strength calculation is slow.(default:on).");
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /* LGS8GXX internal helper functions */
39*4882a593Smuzhiyun 
lgs8gxx_write_reg(struct lgs8gxx_state * priv,u8 reg,u8 data)40*4882a593Smuzhiyun static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	int ret;
43*4882a593Smuzhiyun 	u8 buf[] = { reg, data };
44*4882a593Smuzhiyun 	struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	msg.addr = priv->config->demod_address;
47*4882a593Smuzhiyun 	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
48*4882a593Smuzhiyun 		msg.addr += 0x02;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	if (debug >= 2)
51*4882a593Smuzhiyun 		dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data);
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	ret = i2c_transfer(priv->i2c, &msg, 1);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	if (ret != 1)
56*4882a593Smuzhiyun 		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
57*4882a593Smuzhiyun 			__func__, reg, data, ret);
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	return (ret != 1) ? -1 : 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
lgs8gxx_read_reg(struct lgs8gxx_state * priv,u8 reg,u8 * p_data)62*4882a593Smuzhiyun static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	int ret;
65*4882a593Smuzhiyun 	u8 dev_addr;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	u8 b0[] = { reg };
68*4882a593Smuzhiyun 	u8 b1[] = { 0 };
69*4882a593Smuzhiyun 	struct i2c_msg msg[] = {
70*4882a593Smuzhiyun 		{ .flags = 0, .buf = b0, .len = 1 },
71*4882a593Smuzhiyun 		{ .flags = I2C_M_RD, .buf = b1, .len = 1 },
72*4882a593Smuzhiyun 	};
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	dev_addr = priv->config->demod_address;
75*4882a593Smuzhiyun 	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
76*4882a593Smuzhiyun 		dev_addr += 0x02;
77*4882a593Smuzhiyun 	msg[1].addr =  msg[0].addr = dev_addr;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	ret = i2c_transfer(priv->i2c, msg, 2);
80*4882a593Smuzhiyun 	if (ret != 2) {
81*4882a593Smuzhiyun 		dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret);
82*4882a593Smuzhiyun 		return -1;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	*p_data = b1[0];
86*4882a593Smuzhiyun 	if (debug >= 2)
87*4882a593Smuzhiyun 		dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]);
88*4882a593Smuzhiyun 	return 0;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
lgs8gxx_soft_reset(struct lgs8gxx_state * priv)91*4882a593Smuzhiyun static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x02, 0x00);
94*4882a593Smuzhiyun 	msleep(1);
95*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x02, 0x01);
96*4882a593Smuzhiyun 	msleep(100);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	return 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
wait_reg_mask(struct lgs8gxx_state * priv,u8 reg,u8 mask,u8 val,u8 delay,u8 tries)101*4882a593Smuzhiyun static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
102*4882a593Smuzhiyun 	u8 val, u8 delay, u8 tries)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	u8 t;
105*4882a593Smuzhiyun 	int i;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	for (i = 0; i < tries; i++) {
108*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, reg, &t);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 		if ((t & mask) == val)
111*4882a593Smuzhiyun 			return 0;
112*4882a593Smuzhiyun 		msleep(delay);
113*4882a593Smuzhiyun 	}
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	return 1;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
lgs8gxx_set_ad_mode(struct lgs8gxx_state * priv)118*4882a593Smuzhiyun static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	const struct lgs8gxx_config *config = priv->config;
121*4882a593Smuzhiyun 	u8 if_conf;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if_conf = 0x10; /* AGC output on, RF_AGC output off; */
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	if_conf |=
126*4882a593Smuzhiyun 		((config->ext_adc) ? 0x80 : 0x00) |
127*4882a593Smuzhiyun 		((config->if_neg_center) ? 0x04 : 0x00) |
128*4882a593Smuzhiyun 		((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
129*4882a593Smuzhiyun 		((config->adc_signed) ? 0x02 : 0x00) |
130*4882a593Smuzhiyun 		((config->if_neg_edge) ? 0x01 : 0x00);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	if (config->ext_adc &&
133*4882a593Smuzhiyun 		(config->prod == LGS8GXX_PROD_LGS8G52)) {
134*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xBA, 0x40);
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x07, if_conf);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	return 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
lgs8gxx_set_if_freq(struct lgs8gxx_state * priv,u32 freq)142*4882a593Smuzhiyun static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun 	u64 val;
145*4882a593Smuzhiyun 	u32 v32;
146*4882a593Smuzhiyun 	u32 if_clk;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	if_clk = priv->config->if_clk_freq;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	val = freq;
151*4882a593Smuzhiyun 	if (freq != 0) {
152*4882a593Smuzhiyun 		val <<= 32;
153*4882a593Smuzhiyun 		if (if_clk != 0)
154*4882a593Smuzhiyun 			do_div(val, if_clk);
155*4882a593Smuzhiyun 		v32 = val & 0xFFFFFFFF;
156*4882a593Smuzhiyun 		dprintk("Set IF Freq to %dkHz\n", freq);
157*4882a593Smuzhiyun 	} else {
158*4882a593Smuzhiyun 		v32 = 0;
159*4882a593Smuzhiyun 		dprintk("Set IF Freq to baseband\n");
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 	dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
164*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
165*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
166*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
167*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
168*4882a593Smuzhiyun 	} else {
169*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
170*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
171*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
172*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
lgs8gxx_get_afc_phase(struct lgs8gxx_state * priv)178*4882a593Smuzhiyun static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	u64 val;
181*4882a593Smuzhiyun 	u32 v32 = 0;
182*4882a593Smuzhiyun 	u8 reg_addr, t;
183*4882a593Smuzhiyun 	int i;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
186*4882a593Smuzhiyun 		reg_addr = 0x23;
187*4882a593Smuzhiyun 	else
188*4882a593Smuzhiyun 		reg_addr = 0x48;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
191*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, reg_addr, &t);
192*4882a593Smuzhiyun 		v32 <<= 8;
193*4882a593Smuzhiyun 		v32 |= t;
194*4882a593Smuzhiyun 		reg_addr--;
195*4882a593Smuzhiyun 	}
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	val = v32;
198*4882a593Smuzhiyun 	val *= priv->config->if_clk_freq;
199*4882a593Smuzhiyun 	val >>= 32;
200*4882a593Smuzhiyun 	dprintk("AFC = %u kHz\n", (u32)val);
201*4882a593Smuzhiyun 	return 0;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun 
lgs8gxx_set_mode_auto(struct lgs8gxx_state * priv)204*4882a593Smuzhiyun static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	u8 t;
207*4882a593Smuzhiyun 	u8 prod = priv->config->prod;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (prod == LGS8GXX_PROD_LGS8913)
210*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC6, 0x01);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if (prod == LGS8GXX_PROD_LGS8G75) {
213*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x0C, &t);
214*4882a593Smuzhiyun 		t &= (~0x04);
215*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
216*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x39, 0x00);
217*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x3D, 0x04);
218*4882a593Smuzhiyun 	} else if (prod == LGS8GXX_PROD_LGS8913 ||
219*4882a593Smuzhiyun 		prod == LGS8GXX_PROD_LGS8GL5 ||
220*4882a593Smuzhiyun 		prod == LGS8GXX_PROD_LGS8G42 ||
221*4882a593Smuzhiyun 		prod == LGS8GXX_PROD_LGS8G52 ||
222*4882a593Smuzhiyun 		prod == LGS8GXX_PROD_LGS8G54) {
223*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x7E, &t);
224*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 		/* clear FEC self reset */
227*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0xC5, &t);
228*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	if (prod == LGS8GXX_PROD_LGS8913) {
232*4882a593Smuzhiyun 		/* FEC auto detect */
233*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC1, 0x03);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x7C, &t);
236*4882a593Smuzhiyun 		t = (t & 0x8C) | 0x03;
237*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x7C, t);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 		/* BER test mode */
240*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0xC3, &t);
241*4882a593Smuzhiyun 		t = (t & 0xEF) |  0x10;
242*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC3, t);
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
246*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xD9, 0x40);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
lgs8gxx_set_mode_manual(struct lgs8gxx_state * priv)251*4882a593Smuzhiyun static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	u8 t;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
256*4882a593Smuzhiyun 		u8 t2;
257*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x0C, &t);
258*4882a593Smuzhiyun 		t &= (~0x80);
259*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0C, t);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x0C, &t);
262*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x19, &t2);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 		if (((t&0x03) == 0x01) && (t2&0x01)) {
265*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x6E, 0x05);
266*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x39, 0x02);
267*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x39, 0x03);
268*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x3D, 0x05);
269*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x3E, 0x28);
270*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x53, 0x80);
271*4882a593Smuzhiyun 		} else {
272*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x6E, 0x3F);
273*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x39, 0x00);
274*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x3D, 0x04);
275*4882a593Smuzhiyun 		}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 		lgs8gxx_soft_reset(priv);
278*4882a593Smuzhiyun 		return 0;
279*4882a593Smuzhiyun 	}
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	/* turn off auto-detect; manual settings */
282*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x7E, 0);
283*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
284*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC1, 0);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0xC5, &t);
287*4882a593Smuzhiyun 	t = (t & 0xE0) | 0x06;
288*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0xC5, t);
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	lgs8gxx_soft_reset(priv);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	return 0;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
lgs8gxx_is_locked(struct lgs8gxx_state * priv,u8 * locked)295*4882a593Smuzhiyun static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	int ret = 0;
298*4882a593Smuzhiyun 	u8 t;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
301*4882a593Smuzhiyun 		ret = lgs8gxx_read_reg(priv, 0x13, &t);
302*4882a593Smuzhiyun 	else
303*4882a593Smuzhiyun 		ret = lgs8gxx_read_reg(priv, 0x4B, &t);
304*4882a593Smuzhiyun 	if (ret != 0)
305*4882a593Smuzhiyun 		return ret;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
308*4882a593Smuzhiyun 		*locked = ((t & 0x80) == 0x80) ? 1 : 0;
309*4882a593Smuzhiyun 	else
310*4882a593Smuzhiyun 		*locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
311*4882a593Smuzhiyun 	return 0;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun /* Wait for Code Acquisition Lock */
lgs8gxx_wait_ca_lock(struct lgs8gxx_state * priv,u8 * locked)315*4882a593Smuzhiyun static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	int ret = 0;
318*4882a593Smuzhiyun 	u8 reg, mask, val;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
321*4882a593Smuzhiyun 		reg = 0x13;
322*4882a593Smuzhiyun 		mask = 0x80;
323*4882a593Smuzhiyun 		val = 0x80;
324*4882a593Smuzhiyun 	} else {
325*4882a593Smuzhiyun 		reg = 0x4B;
326*4882a593Smuzhiyun 		mask = 0xC0;
327*4882a593Smuzhiyun 		val = 0xC0;
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
331*4882a593Smuzhiyun 	*locked = (ret == 0) ? 1 : 0;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	return 0;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
lgs8gxx_is_autodetect_finished(struct lgs8gxx_state * priv,u8 * finished)336*4882a593Smuzhiyun static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
337*4882a593Smuzhiyun 					  u8 *finished)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun 	int ret = 0;
340*4882a593Smuzhiyun 	u8 reg, mask, val;
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
343*4882a593Smuzhiyun 		reg = 0x1f;
344*4882a593Smuzhiyun 		mask = 0xC0;
345*4882a593Smuzhiyun 		val = 0x80;
346*4882a593Smuzhiyun 	} else {
347*4882a593Smuzhiyun 		reg = 0xA4;
348*4882a593Smuzhiyun 		mask = 0x03;
349*4882a593Smuzhiyun 		val = 0x01;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
353*4882a593Smuzhiyun 	*finished = (ret == 0) ? 1 : 0;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	return 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun 
lgs8gxx_autolock_gi(struct lgs8gxx_state * priv,u8 gi,u8 cpn,u8 * locked)358*4882a593Smuzhiyun static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
359*4882a593Smuzhiyun 	u8 *locked)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	int err = 0;
362*4882a593Smuzhiyun 	u8 ad_fini = 0;
363*4882a593Smuzhiyun 	u8 t1, t2;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (gi == GI_945)
366*4882a593Smuzhiyun 		dprintk("try GI 945\n");
367*4882a593Smuzhiyun 	else if (gi == GI_595)
368*4882a593Smuzhiyun 		dprintk("try GI 595\n");
369*4882a593Smuzhiyun 	else if (gi == GI_420)
370*4882a593Smuzhiyun 		dprintk("try GI 420\n");
371*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
372*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x0C, &t1);
373*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x18, &t2);
374*4882a593Smuzhiyun 		t1 &= ~(GI_MASK);
375*4882a593Smuzhiyun 		t1 |= gi;
376*4882a593Smuzhiyun 		t2 &= 0xFE;
377*4882a593Smuzhiyun 		t2 |= cpn ? 0x01 : 0x00;
378*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x0C, t1);
379*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x18, t2);
380*4882a593Smuzhiyun 	} else {
381*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x04, gi);
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 	lgs8gxx_soft_reset(priv);
384*4882a593Smuzhiyun 	err = lgs8gxx_wait_ca_lock(priv, locked);
385*4882a593Smuzhiyun 	if (err || !(*locked))
386*4882a593Smuzhiyun 		return err;
387*4882a593Smuzhiyun 	err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
388*4882a593Smuzhiyun 	if (err != 0)
389*4882a593Smuzhiyun 		return err;
390*4882a593Smuzhiyun 	if (ad_fini) {
391*4882a593Smuzhiyun 		dprintk("auto detect finished\n");
392*4882a593Smuzhiyun 	} else
393*4882a593Smuzhiyun 		*locked = 0;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
lgs8gxx_auto_detect(struct lgs8gxx_state * priv,u8 * detected_param,u8 * gi)398*4882a593Smuzhiyun static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
399*4882a593Smuzhiyun 			       u8 *detected_param, u8 *gi)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun 	int i, j;
402*4882a593Smuzhiyun 	int err = 0;
403*4882a593Smuzhiyun 	u8 locked = 0, tmp_gi;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	lgs8gxx_set_mode_auto(priv);
408*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
409*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x67, 0xAA);
410*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x6E, 0x3F);
411*4882a593Smuzhiyun 	} else {
412*4882a593Smuzhiyun 		/* Guard Interval */
413*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x03, 00);
414*4882a593Smuzhiyun 	}
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	for (i = 0; i < 2; i++) {
417*4882a593Smuzhiyun 		for (j = 0; j < 2; j++) {
418*4882a593Smuzhiyun 			tmp_gi = GI_945;
419*4882a593Smuzhiyun 			err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
420*4882a593Smuzhiyun 			if (err)
421*4882a593Smuzhiyun 				goto out;
422*4882a593Smuzhiyun 			if (locked)
423*4882a593Smuzhiyun 				goto locked;
424*4882a593Smuzhiyun 		}
425*4882a593Smuzhiyun 		for (j = 0; j < 2; j++) {
426*4882a593Smuzhiyun 			tmp_gi = GI_420;
427*4882a593Smuzhiyun 			err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
428*4882a593Smuzhiyun 			if (err)
429*4882a593Smuzhiyun 				goto out;
430*4882a593Smuzhiyun 			if (locked)
431*4882a593Smuzhiyun 				goto locked;
432*4882a593Smuzhiyun 		}
433*4882a593Smuzhiyun 		tmp_gi = GI_595;
434*4882a593Smuzhiyun 		err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
435*4882a593Smuzhiyun 		if (err)
436*4882a593Smuzhiyun 			goto out;
437*4882a593Smuzhiyun 		if (locked)
438*4882a593Smuzhiyun 			goto locked;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun locked:
442*4882a593Smuzhiyun 	if ((err == 0) && (locked == 1)) {
443*4882a593Smuzhiyun 		u8 t;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 		if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
446*4882a593Smuzhiyun 			lgs8gxx_read_reg(priv, 0xA2, &t);
447*4882a593Smuzhiyun 			*detected_param = t;
448*4882a593Smuzhiyun 		} else {
449*4882a593Smuzhiyun 			lgs8gxx_read_reg(priv, 0x1F, &t);
450*4882a593Smuzhiyun 			*detected_param = t & 0x3F;
451*4882a593Smuzhiyun 		}
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 		if (tmp_gi == GI_945)
454*4882a593Smuzhiyun 			dprintk("GI 945 locked\n");
455*4882a593Smuzhiyun 		else if (tmp_gi == GI_595)
456*4882a593Smuzhiyun 			dprintk("GI 595 locked\n");
457*4882a593Smuzhiyun 		else if (tmp_gi == GI_420)
458*4882a593Smuzhiyun 			dprintk("GI 420 locked\n");
459*4882a593Smuzhiyun 		*gi = tmp_gi;
460*4882a593Smuzhiyun 	}
461*4882a593Smuzhiyun 	if (!locked)
462*4882a593Smuzhiyun 		err = -1;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun out:
465*4882a593Smuzhiyun 	return err;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
lgs8gxx_auto_lock(struct lgs8gxx_state * priv)468*4882a593Smuzhiyun static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun 	s8 err;
471*4882a593Smuzhiyun 	u8 gi = 0x2;
472*4882a593Smuzhiyun 	u8 detected_param = 0;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	if (err != 0) {
477*4882a593Smuzhiyun 		dprintk("lgs8gxx_auto_detect failed\n");
478*4882a593Smuzhiyun 	} else
479*4882a593Smuzhiyun 		dprintk("detected param = 0x%02X\n", detected_param);
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	/* Apply detected parameters */
482*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
483*4882a593Smuzhiyun 		u8 inter_leave_len = detected_param & TIM_MASK ;
484*4882a593Smuzhiyun 		/* Fix 8913 time interleaver detection bug */
485*4882a593Smuzhiyun 		inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
486*4882a593Smuzhiyun 		detected_param &= CF_MASK | SC_MASK  | LGS_FEC_MASK;
487*4882a593Smuzhiyun 		detected_param |= inter_leave_len;
488*4882a593Smuzhiyun 	}
489*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
490*4882a593Smuzhiyun 		u8 t;
491*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x19, &t);
492*4882a593Smuzhiyun 		t &= 0x81;
493*4882a593Smuzhiyun 		t |= detected_param << 1;
494*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x19, t);
495*4882a593Smuzhiyun 	} else {
496*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x7D, detected_param);
497*4882a593Smuzhiyun 		if (priv->config->prod == LGS8GXX_PROD_LGS8913)
498*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0xC0, detected_param);
499*4882a593Smuzhiyun 	}
500*4882a593Smuzhiyun 	/* lgs8gxx_soft_reset(priv); */
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/* Enter manual mode */
503*4882a593Smuzhiyun 	lgs8gxx_set_mode_manual(priv);
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	switch (gi) {
506*4882a593Smuzhiyun 	case GI_945:
507*4882a593Smuzhiyun 		priv->curr_gi = 945; break;
508*4882a593Smuzhiyun 	case GI_595:
509*4882a593Smuzhiyun 		priv->curr_gi = 595; break;
510*4882a593Smuzhiyun 	case GI_420:
511*4882a593Smuzhiyun 		priv->curr_gi = 420; break;
512*4882a593Smuzhiyun 	default:
513*4882a593Smuzhiyun 		priv->curr_gi = 945; break;
514*4882a593Smuzhiyun 	}
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
lgs8gxx_set_mpeg_mode(struct lgs8gxx_state * priv,u8 serial,u8 clk_pol,u8 clk_gated)517*4882a593Smuzhiyun static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
518*4882a593Smuzhiyun 	u8 serial, u8 clk_pol, u8 clk_gated)
519*4882a593Smuzhiyun {
520*4882a593Smuzhiyun 	int ret = 0;
521*4882a593Smuzhiyun 	u8 t, reg_addr;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
524*4882a593Smuzhiyun 	ret = lgs8gxx_read_reg(priv, reg_addr, &t);
525*4882a593Smuzhiyun 	if (ret != 0)
526*4882a593Smuzhiyun 		return ret;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	t &= 0xF8;
529*4882a593Smuzhiyun 	t |= serial ? TS_SERIAL : TS_PARALLEL;
530*4882a593Smuzhiyun 	t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
531*4882a593Smuzhiyun 	t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	ret = lgs8gxx_write_reg(priv, reg_addr, t);
534*4882a593Smuzhiyun 	if (ret != 0)
535*4882a593Smuzhiyun 		return ret;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	return 0;
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun /* A/D input peak-to-peak voltage range */
lgs8g75_set_adc_vpp(struct lgs8gxx_state * priv,u8 sel)541*4882a593Smuzhiyun static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
542*4882a593Smuzhiyun 	u8 sel)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun 	u8 r26 = 0x73, r27 = 0x90;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
547*4882a593Smuzhiyun 		return 0;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	r26 |= (sel & 0x01) << 7;
550*4882a593Smuzhiyun 	r27 |= (sel & 0x02) >> 1;
551*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x26, r26);
552*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x27, r27);
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	return 0;
555*4882a593Smuzhiyun }
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun /* LGS8913 demod frontend functions */
558*4882a593Smuzhiyun 
lgs8913_init(struct lgs8gxx_state * priv)559*4882a593Smuzhiyun static int lgs8913_init(struct lgs8gxx_state *priv)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	u8 t;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	/* LGS8913 specific */
564*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0xc1, 0x3);
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0x7c, &t);
567*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	/* LGS8913 specific */
570*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0xc3, &t);
571*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0xc3, t&0x10);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	return 0;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun 
lgs8g75_init_data(struct lgs8gxx_state * priv)577*4882a593Smuzhiyun static int lgs8g75_init_data(struct lgs8gxx_state *priv)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun 	const struct firmware *fw;
580*4882a593Smuzhiyun 	int rc;
581*4882a593Smuzhiyun 	int i;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
584*4882a593Smuzhiyun 	if (rc)
585*4882a593Smuzhiyun 		return rc;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0xC6, 0x40);
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x3D, 0x04);
590*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x39, 0x00);
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x3A, 0x00);
593*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x38, 0x00);
594*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x3B, 0x00);
595*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x38, 0x00);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	for (i = 0; i < fw->size; i++) {
598*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x38, 0x00);
599*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
600*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
601*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x3C, fw->data[i]);
602*4882a593Smuzhiyun 	}
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	lgs8gxx_write_reg(priv, 0x38, 0x00);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	release_firmware(fw);
607*4882a593Smuzhiyun 	return 0;
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun 
lgs8gxx_init(struct dvb_frontend * fe)610*4882a593Smuzhiyun static int lgs8gxx_init(struct dvb_frontend *fe)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun 	struct lgs8gxx_state *priv =
613*4882a593Smuzhiyun 		(struct lgs8gxx_state *)fe->demodulator_priv;
614*4882a593Smuzhiyun 	const struct lgs8gxx_config *config = priv->config;
615*4882a593Smuzhiyun 	u8 data = 0;
616*4882a593Smuzhiyun 	s8 err;
617*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0, &data);
620*4882a593Smuzhiyun 	dprintk("reg 0 = 0x%02X\n", data);
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	if (config->prod == LGS8GXX_PROD_LGS8G75)
623*4882a593Smuzhiyun 		lgs8g75_set_adc_vpp(priv, config->adc_vpp);
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 	/* Setup MPEG output format */
626*4882a593Smuzhiyun 	err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
627*4882a593Smuzhiyun 				    config->ts_clk_pol,
628*4882a593Smuzhiyun 				    config->ts_clk_gated);
629*4882a593Smuzhiyun 	if (err != 0)
630*4882a593Smuzhiyun 		return -EIO;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	if (config->prod == LGS8GXX_PROD_LGS8913)
633*4882a593Smuzhiyun 		lgs8913_init(priv);
634*4882a593Smuzhiyun 	lgs8gxx_set_if_freq(priv, priv->config->if_freq);
635*4882a593Smuzhiyun 	lgs8gxx_set_ad_mode(priv);
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	return 0;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun 
lgs8gxx_release(struct dvb_frontend * fe)640*4882a593Smuzhiyun static void lgs8gxx_release(struct dvb_frontend *fe)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun 	struct lgs8gxx_state *state = fe->demodulator_priv;
643*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	kfree(state);
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 
lgs8gxx_write(struct dvb_frontend * fe,const u8 buf[],int len)649*4882a593Smuzhiyun static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
650*4882a593Smuzhiyun {
651*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (len != 2)
654*4882a593Smuzhiyun 		return -EINVAL;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	return lgs8gxx_write_reg(priv, buf[0], buf[1]);
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun 
lgs8gxx_set_fe(struct dvb_frontend * fe)659*4882a593Smuzhiyun static int lgs8gxx_set_fe(struct dvb_frontend *fe)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun 	struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
662*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	/* set frequency */
667*4882a593Smuzhiyun 	if (fe->ops.tuner_ops.set_params) {
668*4882a593Smuzhiyun 		fe->ops.tuner_ops.set_params(fe);
669*4882a593Smuzhiyun 		if (fe->ops.i2c_gate_ctrl)
670*4882a593Smuzhiyun 			fe->ops.i2c_gate_ctrl(fe, 0);
671*4882a593Smuzhiyun 	}
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	/* start auto lock */
674*4882a593Smuzhiyun 	lgs8gxx_auto_lock(priv);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	msleep(10);
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	/* TODO: get real readings from device */
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	/* bandwidth */
681*4882a593Smuzhiyun 	fe_params->bandwidth_hz = 8000000;
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	fe_params->code_rate_HP = FEC_AUTO;
684*4882a593Smuzhiyun 	fe_params->code_rate_LP = FEC_AUTO;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	fe_params->modulation = QAM_AUTO;
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	/* transmission mode */
689*4882a593Smuzhiyun 	fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	/* guard interval */
692*4882a593Smuzhiyun 	fe_params->guard_interval = GUARD_INTERVAL_AUTO;
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	/* hierarchy */
695*4882a593Smuzhiyun 	fe_params->hierarchy = HIERARCHY_NONE;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	return 0;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun static
lgs8gxx_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * fesettings)701*4882a593Smuzhiyun int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
702*4882a593Smuzhiyun 			      struct dvb_frontend_tune_settings *fesettings)
703*4882a593Smuzhiyun {
704*4882a593Smuzhiyun 	/* FIXME: copy from tda1004x.c */
705*4882a593Smuzhiyun 	fesettings->min_delay_ms = 800;
706*4882a593Smuzhiyun 	fesettings->step_size = 0;
707*4882a593Smuzhiyun 	fesettings->max_drift = 0;
708*4882a593Smuzhiyun 	return 0;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun 
lgs8gxx_read_status(struct dvb_frontend * fe,enum fe_status * fe_status)711*4882a593Smuzhiyun static int lgs8gxx_read_status(struct dvb_frontend *fe,
712*4882a593Smuzhiyun 			       enum fe_status *fe_status)
713*4882a593Smuzhiyun {
714*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
715*4882a593Smuzhiyun 	s8 ret;
716*4882a593Smuzhiyun 	u8 t, locked = 0;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
719*4882a593Smuzhiyun 	*fe_status = 0;
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	lgs8gxx_get_afc_phase(priv);
722*4882a593Smuzhiyun 	lgs8gxx_is_locked(priv, &locked);
723*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
724*4882a593Smuzhiyun 		if (locked)
725*4882a593Smuzhiyun 			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
726*4882a593Smuzhiyun 				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
727*4882a593Smuzhiyun 		return 0;
728*4882a593Smuzhiyun 	}
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
731*4882a593Smuzhiyun 	if (ret != 0)
732*4882a593Smuzhiyun 		return -EIO;
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	dprintk("Reg 0x4B: 0x%02X\n", t);
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	*fe_status = 0;
737*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
738*4882a593Smuzhiyun 		if ((t & 0x40) == 0x40)
739*4882a593Smuzhiyun 			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
740*4882a593Smuzhiyun 		if ((t & 0x80) == 0x80)
741*4882a593Smuzhiyun 			*fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
742*4882a593Smuzhiyun 				FE_HAS_LOCK;
743*4882a593Smuzhiyun 	} else {
744*4882a593Smuzhiyun 		if ((t & 0x80) == 0x80)
745*4882a593Smuzhiyun 			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
746*4882a593Smuzhiyun 				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
747*4882a593Smuzhiyun 	}
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun 	/* success */
750*4882a593Smuzhiyun 	dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
751*4882a593Smuzhiyun 	return 0;
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun 
lgs8gxx_read_signal_agc(struct lgs8gxx_state * priv,u16 * signal)754*4882a593Smuzhiyun static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
755*4882a593Smuzhiyun {
756*4882a593Smuzhiyun 	u16 v;
757*4882a593Smuzhiyun 	u8 agc_lvl[2], cat;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	dprintk("%s()\n", __func__);
760*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
761*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	v = agc_lvl[0];
764*4882a593Smuzhiyun 	v <<= 8;
765*4882a593Smuzhiyun 	v |= agc_lvl[1];
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	dprintk("agc_lvl: 0x%04X\n", v);
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	if (v < 0x100)
770*4882a593Smuzhiyun 		cat = 0;
771*4882a593Smuzhiyun 	else if (v < 0x190)
772*4882a593Smuzhiyun 		cat = 5;
773*4882a593Smuzhiyun 	else if (v < 0x2A8)
774*4882a593Smuzhiyun 		cat = 4;
775*4882a593Smuzhiyun 	else if (v < 0x381)
776*4882a593Smuzhiyun 		cat = 3;
777*4882a593Smuzhiyun 	else if (v < 0x400)
778*4882a593Smuzhiyun 		cat = 2;
779*4882a593Smuzhiyun 	else if (v == 0x400)
780*4882a593Smuzhiyun 		cat = 1;
781*4882a593Smuzhiyun 	else
782*4882a593Smuzhiyun 		cat = 0;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	*signal = cat * 65535 / 5;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	return 0;
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun 
lgs8913_read_signal_strength(struct lgs8gxx_state * priv,u16 * signal)789*4882a593Smuzhiyun static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun 	u8 t; s8 ret;
792*4882a593Smuzhiyun 	s16 max_strength = 0;
793*4882a593Smuzhiyun 	u8 str;
794*4882a593Smuzhiyun 	u16 i, gi = priv->curr_gi;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
799*4882a593Smuzhiyun 	if (ret != 0)
800*4882a593Smuzhiyun 		return -EIO;
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	if (fake_signal_str) {
803*4882a593Smuzhiyun 		if ((t & 0xC0) == 0xC0) {
804*4882a593Smuzhiyun 			dprintk("Fake signal strength\n");
805*4882a593Smuzhiyun 			*signal = 0x7FFF;
806*4882a593Smuzhiyun 		} else
807*4882a593Smuzhiyun 			*signal = 0;
808*4882a593Smuzhiyun 		return 0;
809*4882a593Smuzhiyun 	}
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun 	dprintk("gi = %d\n", gi);
812*4882a593Smuzhiyun 	for (i = 0; i < gi; i++) {
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 		if ((i & 0xFF) == 0)
815*4882a593Smuzhiyun 			lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
816*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x94, &str);
819*4882a593Smuzhiyun 		if (max_strength < str)
820*4882a593Smuzhiyun 			max_strength = str;
821*4882a593Smuzhiyun 	}
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	*signal = max_strength;
824*4882a593Smuzhiyun 	dprintk("%s: signal=0x%02X\n", __func__, *signal);
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0x95, &t);
827*4882a593Smuzhiyun 	dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	return 0;
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun 
lgs8g75_read_signal_strength(struct lgs8gxx_state * priv,u16 * signal)832*4882a593Smuzhiyun static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
833*4882a593Smuzhiyun {
834*4882a593Smuzhiyun 	u8 t;
835*4882a593Smuzhiyun 	s16 v = 0;
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0xB1, &t);
840*4882a593Smuzhiyun 	v |= t;
841*4882a593Smuzhiyun 	v <<= 8;
842*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 0xB0, &t);
843*4882a593Smuzhiyun 	v |= t;
844*4882a593Smuzhiyun 
845*4882a593Smuzhiyun 	*signal = v;
846*4882a593Smuzhiyun 	dprintk("%s: signal=0x%02X\n", __func__, *signal);
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	return 0;
849*4882a593Smuzhiyun }
850*4882a593Smuzhiyun 
lgs8gxx_read_signal_strength(struct dvb_frontend * fe,u16 * signal)851*4882a593Smuzhiyun static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
852*4882a593Smuzhiyun {
853*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
854*4882a593Smuzhiyun 
855*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
856*4882a593Smuzhiyun 		return lgs8913_read_signal_strength(priv, signal);
857*4882a593Smuzhiyun 	else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
858*4882a593Smuzhiyun 		return lgs8g75_read_signal_strength(priv, signal);
859*4882a593Smuzhiyun 	else
860*4882a593Smuzhiyun 		return lgs8gxx_read_signal_agc(priv, signal);
861*4882a593Smuzhiyun }
862*4882a593Smuzhiyun 
lgs8gxx_read_snr(struct dvb_frontend * fe,u16 * snr)863*4882a593Smuzhiyun static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
864*4882a593Smuzhiyun {
865*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
866*4882a593Smuzhiyun 	u8 t;
867*4882a593Smuzhiyun 	*snr = 0;
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
870*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x34, &t);
871*4882a593Smuzhiyun 	else
872*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x95, &t);
873*4882a593Smuzhiyun 	dprintk("AVG Noise=0x%02X\n", t);
874*4882a593Smuzhiyun 	*snr = 256 - t;
875*4882a593Smuzhiyun 	*snr <<= 8;
876*4882a593Smuzhiyun 	dprintk("snr=0x%x\n", *snr);
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 	return 0;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun 
lgs8gxx_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)881*4882a593Smuzhiyun static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
882*4882a593Smuzhiyun {
883*4882a593Smuzhiyun 	*ucblocks = 0;
884*4882a593Smuzhiyun 	dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
885*4882a593Smuzhiyun 	return 0;
886*4882a593Smuzhiyun }
887*4882a593Smuzhiyun 
packet_counter_start(struct lgs8gxx_state * priv)888*4882a593Smuzhiyun static void packet_counter_start(struct lgs8gxx_state *priv)
889*4882a593Smuzhiyun {
890*4882a593Smuzhiyun 	u8 orig, t;
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
893*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x30, &orig);
894*4882a593Smuzhiyun 		orig &= 0xE7;
895*4882a593Smuzhiyun 		t = orig | 0x10;
896*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x30, t);
897*4882a593Smuzhiyun 		t = orig | 0x18;
898*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x30, t);
899*4882a593Smuzhiyun 		t = orig | 0x10;
900*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x30, t);
901*4882a593Smuzhiyun 	} else {
902*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC6, 0x01);
903*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC6, 0x41);
904*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC6, 0x01);
905*4882a593Smuzhiyun 	}
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun 
packet_counter_stop(struct lgs8gxx_state * priv)908*4882a593Smuzhiyun static void packet_counter_stop(struct lgs8gxx_state *priv)
909*4882a593Smuzhiyun {
910*4882a593Smuzhiyun 	u8 t;
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
913*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, 0x30, &t);
914*4882a593Smuzhiyun 		t &= 0xE7;
915*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0x30, t);
916*4882a593Smuzhiyun 	} else {
917*4882a593Smuzhiyun 		lgs8gxx_write_reg(priv, 0xC6, 0x81);
918*4882a593Smuzhiyun 	}
919*4882a593Smuzhiyun }
920*4882a593Smuzhiyun 
lgs8gxx_read_ber(struct dvb_frontend * fe,u32 * ber)921*4882a593Smuzhiyun static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
922*4882a593Smuzhiyun {
923*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
924*4882a593Smuzhiyun 	u8 reg_err, reg_total, t;
925*4882a593Smuzhiyun 	u32 total_cnt = 0, err_cnt = 0;
926*4882a593Smuzhiyun 	int i;
927*4882a593Smuzhiyun 
928*4882a593Smuzhiyun 	dprintk("%s\n", __func__);
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	packet_counter_start(priv);
931*4882a593Smuzhiyun 	msleep(200);
932*4882a593Smuzhiyun 	packet_counter_stop(priv);
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
935*4882a593Smuzhiyun 		reg_total = 0x28; reg_err = 0x2C;
936*4882a593Smuzhiyun 	} else {
937*4882a593Smuzhiyun 		reg_total = 0xD0; reg_err = 0xD4;
938*4882a593Smuzhiyun 	}
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
941*4882a593Smuzhiyun 		total_cnt <<= 8;
942*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, reg_total+3-i, &t);
943*4882a593Smuzhiyun 		total_cnt |= t;
944*4882a593Smuzhiyun 	}
945*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
946*4882a593Smuzhiyun 		err_cnt <<= 8;
947*4882a593Smuzhiyun 		lgs8gxx_read_reg(priv, reg_err+3-i, &t);
948*4882a593Smuzhiyun 		err_cnt |= t;
949*4882a593Smuzhiyun 	}
950*4882a593Smuzhiyun 	dprintk("error=%d total=%d\n", err_cnt, total_cnt);
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	if (total_cnt == 0)
953*4882a593Smuzhiyun 		*ber = 0;
954*4882a593Smuzhiyun 	else
955*4882a593Smuzhiyun 		*ber = err_cnt * 100 / total_cnt;
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	dprintk("%s: ber=0x%x\n", __func__, *ber);
958*4882a593Smuzhiyun 	return 0;
959*4882a593Smuzhiyun }
960*4882a593Smuzhiyun 
lgs8gxx_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)961*4882a593Smuzhiyun static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
962*4882a593Smuzhiyun {
963*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = fe->demodulator_priv;
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 	if (priv->config->tuner_address == 0)
966*4882a593Smuzhiyun 		return 0;
967*4882a593Smuzhiyun 	if (enable) {
968*4882a593Smuzhiyun 		u8 v = 0x80 | priv->config->tuner_address;
969*4882a593Smuzhiyun 		return lgs8gxx_write_reg(priv, 0x01, v);
970*4882a593Smuzhiyun 	}
971*4882a593Smuzhiyun 	return lgs8gxx_write_reg(priv, 0x01, 0);
972*4882a593Smuzhiyun }
973*4882a593Smuzhiyun 
974*4882a593Smuzhiyun static const struct dvb_frontend_ops lgs8gxx_ops = {
975*4882a593Smuzhiyun 	.delsys = { SYS_DTMB },
976*4882a593Smuzhiyun 	.info = {
977*4882a593Smuzhiyun 		.name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
978*4882a593Smuzhiyun 		.frequency_min_hz = 474 * MHz,
979*4882a593Smuzhiyun 		.frequency_max_hz = 858 * MHz,
980*4882a593Smuzhiyun 		.frequency_stepsize_hz = 10 * kHz,
981*4882a593Smuzhiyun 		.caps =
982*4882a593Smuzhiyun 			FE_CAN_FEC_AUTO |
983*4882a593Smuzhiyun 			FE_CAN_QAM_AUTO |
984*4882a593Smuzhiyun 			FE_CAN_TRANSMISSION_MODE_AUTO |
985*4882a593Smuzhiyun 			FE_CAN_GUARD_INTERVAL_AUTO
986*4882a593Smuzhiyun 	},
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	.release = lgs8gxx_release,
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	.init = lgs8gxx_init,
991*4882a593Smuzhiyun 	.write = lgs8gxx_write,
992*4882a593Smuzhiyun 	.i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 	.set_frontend = lgs8gxx_set_fe,
995*4882a593Smuzhiyun 	.get_tune_settings = lgs8gxx_get_tune_settings,
996*4882a593Smuzhiyun 
997*4882a593Smuzhiyun 	.read_status = lgs8gxx_read_status,
998*4882a593Smuzhiyun 	.read_ber = lgs8gxx_read_ber,
999*4882a593Smuzhiyun 	.read_signal_strength = lgs8gxx_read_signal_strength,
1000*4882a593Smuzhiyun 	.read_snr = lgs8gxx_read_snr,
1001*4882a593Smuzhiyun 	.read_ucblocks = lgs8gxx_read_ucblocks,
1002*4882a593Smuzhiyun };
1003*4882a593Smuzhiyun 
lgs8gxx_attach(const struct lgs8gxx_config * config,struct i2c_adapter * i2c)1004*4882a593Smuzhiyun struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
1005*4882a593Smuzhiyun 	struct i2c_adapter *i2c)
1006*4882a593Smuzhiyun {
1007*4882a593Smuzhiyun 	struct lgs8gxx_state *priv = NULL;
1008*4882a593Smuzhiyun 	u8 data = 0;
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	dprintk("%s()\n", __func__);
1011*4882a593Smuzhiyun 
1012*4882a593Smuzhiyun 	if (config == NULL || i2c == NULL)
1013*4882a593Smuzhiyun 		return NULL;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
1016*4882a593Smuzhiyun 	if (priv == NULL)
1017*4882a593Smuzhiyun 		goto error_out;
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	priv->config = config;
1020*4882a593Smuzhiyun 	priv->i2c = i2c;
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	/* check if the demod is there */
1023*4882a593Smuzhiyun 	if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
1024*4882a593Smuzhiyun 		dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
1025*4882a593Smuzhiyun 			__func__, priv->config->demod_address);
1026*4882a593Smuzhiyun 		goto error_out;
1027*4882a593Smuzhiyun 	}
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	lgs8gxx_read_reg(priv, 1, &data);
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	memcpy(&priv->frontend.ops, &lgs8gxx_ops,
1032*4882a593Smuzhiyun 	       sizeof(struct dvb_frontend_ops));
1033*4882a593Smuzhiyun 	priv->frontend.demodulator_priv = priv;
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	if (config->prod == LGS8GXX_PROD_LGS8G75)
1036*4882a593Smuzhiyun 		lgs8g75_init_data(priv);
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	return &priv->frontend;
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun error_out:
1041*4882a593Smuzhiyun 	dprintk("%s() error_out\n", __func__);
1042*4882a593Smuzhiyun 	kfree(priv);
1043*4882a593Smuzhiyun 	return NULL;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun }
1046*4882a593Smuzhiyun EXPORT_SYMBOL(lgs8gxx_attach);
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
1049*4882a593Smuzhiyun MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
1050*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1051*4882a593Smuzhiyun MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
1052