xref: /linux/drivers/media/dvb-frontends/stv0288.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e4aab64cSIgor M. Liplianin /*
3e4aab64cSIgor M. Liplianin 	Driver for ST STV0288 demodulator
4e4aab64cSIgor M. Liplianin 	Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
5e4aab64cSIgor M. Liplianin 		for Reel Multimedia
6e4aab64cSIgor M. Liplianin 	Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com>
7e4aab64cSIgor M. Liplianin 	Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
8e4aab64cSIgor M. Liplianin 		Removed stb6000 specific tuner code and revised some
9e4aab64cSIgor M. Liplianin 		procedures.
10352a587cSMalcolm Priestley 	2010-09-01 Josef Pavlik <josef@pavlik.it>
11352a587cSMalcolm Priestley 		Fixed diseqc_msg, diseqc_burst and set_tone problems
12e4aab64cSIgor M. Liplianin 
13e4aab64cSIgor M. Liplianin 
14e4aab64cSIgor M. Liplianin */
15e4aab64cSIgor M. Liplianin 
16e4aab64cSIgor M. Liplianin #include <linux/init.h>
17e4aab64cSIgor M. Liplianin #include <linux/kernel.h>
18e4aab64cSIgor M. Liplianin #include <linux/module.h>
19e4aab64cSIgor M. Liplianin #include <linux/string.h>
20e4aab64cSIgor M. Liplianin #include <linux/slab.h>
21e4aab64cSIgor M. Liplianin #include <linux/jiffies.h>
22e4aab64cSIgor M. Liplianin #include <asm/div64.h>
23e4aab64cSIgor M. Liplianin 
24fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
25e4aab64cSIgor M. Liplianin #include "stv0288.h"
26e4aab64cSIgor M. Liplianin 
27e4aab64cSIgor M. Liplianin struct stv0288_state {
28e4aab64cSIgor M. Liplianin 	struct i2c_adapter *i2c;
29e4aab64cSIgor M. Liplianin 	const struct stv0288_config *config;
30e4aab64cSIgor M. Liplianin 	struct dvb_frontend frontend;
31e4aab64cSIgor M. Liplianin 
32e4aab64cSIgor M. Liplianin 	u8 initialised:1;
33e4aab64cSIgor M. Liplianin 	u32 tuner_frequency;
34e4aab64cSIgor M. Liplianin 	u32 symbol_rate;
350df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec_inner;
36e4aab64cSIgor M. Liplianin 	int errmode;
37e4aab64cSIgor M. Liplianin };
38e4aab64cSIgor M. Liplianin 
39e4aab64cSIgor M. Liplianin #define STATUS_BER 0
40e4aab64cSIgor M. Liplianin #define STATUS_UCBLOCKS 1
41e4aab64cSIgor M. Liplianin 
42e4aab64cSIgor M. Liplianin static int debug;
43e4aab64cSIgor M. Liplianin static int debug_legacy_dish_switch;
44e4aab64cSIgor M. Liplianin #define dprintk(args...) \
45e4aab64cSIgor M. Liplianin 	do { \
46e4aab64cSIgor M. Liplianin 		if (debug) \
47e4aab64cSIgor M. Liplianin 			printk(KERN_DEBUG "stv0288: " args); \
48e4aab64cSIgor M. Liplianin 	} while (0)
49e4aab64cSIgor M. Liplianin 
50e4aab64cSIgor M. Liplianin 
51e4aab64cSIgor M. Liplianin static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data)
52e4aab64cSIgor M. Liplianin {
53e4aab64cSIgor M. Liplianin 	int ret;
54e4aab64cSIgor M. Liplianin 	u8 buf[] = { reg, data };
55e4aab64cSIgor M. Liplianin 	struct i2c_msg msg = {
56e4aab64cSIgor M. Liplianin 		.addr = state->config->demod_address,
57e4aab64cSIgor M. Liplianin 		.flags = 0,
58e4aab64cSIgor M. Liplianin 		.buf = buf,
59e4aab64cSIgor M. Liplianin 		.len = 2
60e4aab64cSIgor M. Liplianin 	};
61e4aab64cSIgor M. Liplianin 
62e4aab64cSIgor M. Liplianin 	ret = i2c_transfer(state->i2c, &msg, 1);
63e4aab64cSIgor M. Liplianin 
64e4aab64cSIgor M. Liplianin 	if (ret != 1)
654bd69e7bSMauro Carvalho Chehab 		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
664bd69e7bSMauro Carvalho Chehab 			__func__, reg, data, ret);
67e4aab64cSIgor M. Liplianin 
68e4aab64cSIgor M. Liplianin 	return (ret != 1) ? -EREMOTEIO : 0;
69e4aab64cSIgor M. Liplianin }
70e4aab64cSIgor M. Liplianin 
712e4e98e7Slawrence rust static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len)
72e4aab64cSIgor M. Liplianin {
73e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
74e4aab64cSIgor M. Liplianin 
75e4aab64cSIgor M. Liplianin 	if (len != 2)
76e4aab64cSIgor M. Liplianin 		return -EINVAL;
77e4aab64cSIgor M. Liplianin 
78e4aab64cSIgor M. Liplianin 	return stv0288_writeregI(state, buf[0], buf[1]);
79e4aab64cSIgor M. Liplianin }
80e4aab64cSIgor M. Liplianin 
81e4aab64cSIgor M. Liplianin static u8 stv0288_readreg(struct stv0288_state *state, u8 reg)
82e4aab64cSIgor M. Liplianin {
83e4aab64cSIgor M. Liplianin 	int ret;
84e4aab64cSIgor M. Liplianin 	u8 b0[] = { reg };
85e4aab64cSIgor M. Liplianin 	u8 b1[] = { 0 };
86e4aab64cSIgor M. Liplianin 	struct i2c_msg msg[] = {
87e4aab64cSIgor M. Liplianin 		{
88e4aab64cSIgor M. Liplianin 			.addr = state->config->demod_address,
89e4aab64cSIgor M. Liplianin 			.flags = 0,
90e4aab64cSIgor M. Liplianin 			.buf = b0,
91e4aab64cSIgor M. Liplianin 			.len = 1
92e4aab64cSIgor M. Liplianin 		}, {
93e4aab64cSIgor M. Liplianin 			.addr = state->config->demod_address,
94e4aab64cSIgor M. Liplianin 			.flags = I2C_M_RD,
95e4aab64cSIgor M. Liplianin 			.buf = b1,
96e4aab64cSIgor M. Liplianin 			.len = 1
97e4aab64cSIgor M. Liplianin 		}
98e4aab64cSIgor M. Liplianin 	};
99e4aab64cSIgor M. Liplianin 
100e4aab64cSIgor M. Liplianin 	ret = i2c_transfer(state->i2c, msg, 2);
101e4aab64cSIgor M. Liplianin 
102e4aab64cSIgor M. Liplianin 	if (ret != 2)
103e4aab64cSIgor M. Liplianin 		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
104e4aab64cSIgor M. Liplianin 				__func__, reg, ret);
105e4aab64cSIgor M. Liplianin 
106e4aab64cSIgor M. Liplianin 	return b1[0];
107e4aab64cSIgor M. Liplianin }
108e4aab64cSIgor M. Liplianin 
109e4aab64cSIgor M. Liplianin static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
110e4aab64cSIgor M. Liplianin {
111e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
112e4aab64cSIgor M. Liplianin 	unsigned int temp;
113e4aab64cSIgor M. Liplianin 	unsigned char b[3];
114e4aab64cSIgor M. Liplianin 
115e4aab64cSIgor M. Liplianin 	if ((srate < 1000000) || (srate > 45000000))
116e4aab64cSIgor M. Liplianin 		return -EINVAL;
117e4aab64cSIgor M. Liplianin 
11859152b1cStvboxspy 	stv0288_writeregI(state, 0x22, 0);
11959152b1cStvboxspy 	stv0288_writeregI(state, 0x23, 0);
12059152b1cStvboxspy 	stv0288_writeregI(state, 0x2b, 0xff);
12159152b1cStvboxspy 	stv0288_writeregI(state, 0x2c, 0xf7);
12259152b1cStvboxspy 
123e4aab64cSIgor M. Liplianin 	temp = (unsigned int)srate / 1000;
124e4aab64cSIgor M. Liplianin 
125e4aab64cSIgor M. Liplianin 	temp = temp * 32768;
126e4aab64cSIgor M. Liplianin 	temp = temp / 25;
127e4aab64cSIgor M. Liplianin 	temp = temp / 125;
128e4aab64cSIgor M. Liplianin 	b[0] = (unsigned char)((temp >> 12) & 0xff);
129e4aab64cSIgor M. Liplianin 	b[1] = (unsigned char)((temp >> 4) & 0xff);
130e4aab64cSIgor M. Liplianin 	b[2] = (unsigned char)((temp << 4) & 0xf0);
131e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x28, 0x80); /* SFRH */
132e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x29, 0); /* SFRM */
133e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x2a, 0); /* SFRL */
134e4aab64cSIgor M. Liplianin 
135e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x28, b[0]);
136e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x29, b[1]);
137e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x2a, b[2]);
138e4aab64cSIgor M. Liplianin 	dprintk("stv0288: stv0288_set_symbolrate\n");
139e4aab64cSIgor M. Liplianin 
140e4aab64cSIgor M. Liplianin 	return 0;
141e4aab64cSIgor M. Liplianin }
142e4aab64cSIgor M. Liplianin 
143e4aab64cSIgor M. Liplianin static int stv0288_send_diseqc_msg(struct dvb_frontend *fe,
144e4aab64cSIgor M. Liplianin 				    struct dvb_diseqc_master_cmd *m)
145e4aab64cSIgor M. Liplianin {
146e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
147e4aab64cSIgor M. Liplianin 
148e4aab64cSIgor M. Liplianin 	int i;
149e4aab64cSIgor M. Liplianin 
150e4aab64cSIgor M. Liplianin 	dprintk("%s\n", __func__);
151e4aab64cSIgor M. Liplianin 
152e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x09, 0);
153e4aab64cSIgor M. Liplianin 	msleep(30);
154352a587cSMalcolm Priestley 	stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */
155e4aab64cSIgor M. Liplianin 
156e4aab64cSIgor M. Liplianin 	for (i = 0; i < m->msg_len; i++) {
157e4aab64cSIgor M. Liplianin 		if (stv0288_writeregI(state, 0x06, m->msg[i]))
158e4aab64cSIgor M. Liplianin 			return -EREMOTEIO;
159e4aab64cSIgor M. Liplianin 	}
160352a587cSMalcolm Priestley 	msleep(m->msg_len*12);
161e4aab64cSIgor M. Liplianin 	return 0;
162e4aab64cSIgor M. Liplianin }
163e4aab64cSIgor M. Liplianin 
164e4aab64cSIgor M. Liplianin static int stv0288_send_diseqc_burst(struct dvb_frontend *fe,
1650df289a2SMauro Carvalho Chehab 				     enum fe_sec_mini_cmd burst)
166e4aab64cSIgor M. Liplianin {
167e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
168e4aab64cSIgor M. Liplianin 
169e4aab64cSIgor M. Liplianin 	dprintk("%s\n", __func__);
170e4aab64cSIgor M. Liplianin 
171352a587cSMalcolm Priestley 	if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */
172e4aab64cSIgor M. Liplianin 		return -EREMOTEIO;
173e4aab64cSIgor M. Liplianin 
174e4aab64cSIgor M. Liplianin 	if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
175e4aab64cSIgor M. Liplianin 		return -EREMOTEIO;
176e4aab64cSIgor M. Liplianin 
177352a587cSMalcolm Priestley 	msleep(15);
178352a587cSMalcolm Priestley 	if (stv0288_writeregI(state, 0x05, 0x12))
179e4aab64cSIgor M. Liplianin 		return -EREMOTEIO;
180e4aab64cSIgor M. Liplianin 
181e4aab64cSIgor M. Liplianin 	return 0;
182e4aab64cSIgor M. Liplianin }
183e4aab64cSIgor M. Liplianin 
1840df289a2SMauro Carvalho Chehab static int stv0288_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
185e4aab64cSIgor M. Liplianin {
186e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
187e4aab64cSIgor M. Liplianin 
188e4aab64cSIgor M. Liplianin 	switch (tone) {
189e4aab64cSIgor M. Liplianin 	case SEC_TONE_ON:
190352a587cSMalcolm Priestley 		if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */
191e4aab64cSIgor M. Liplianin 			return -EREMOTEIO;
192352a587cSMalcolm Priestley 	break;
193e4aab64cSIgor M. Liplianin 
194e4aab64cSIgor M. Liplianin 	case SEC_TONE_OFF:
195352a587cSMalcolm Priestley 		if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/
196e4aab64cSIgor M. Liplianin 			return -EREMOTEIO;
197352a587cSMalcolm Priestley 	break;
198e4aab64cSIgor M. Liplianin 
199e4aab64cSIgor M. Liplianin 	default:
200e4aab64cSIgor M. Liplianin 		return -EINVAL;
201e4aab64cSIgor M. Liplianin 	}
202352a587cSMalcolm Priestley 	return 0;
203e4aab64cSIgor M. Liplianin }
204e4aab64cSIgor M. Liplianin 
205e4aab64cSIgor M. Liplianin static u8 stv0288_inittab[] = {
206e4aab64cSIgor M. Liplianin 	0x01, 0x15,
207e4aab64cSIgor M. Liplianin 	0x02, 0x20,
208e4aab64cSIgor M. Liplianin 	0x09, 0x0,
209e4aab64cSIgor M. Liplianin 	0x0a, 0x4,
210e4aab64cSIgor M. Liplianin 	0x0b, 0x0,
211e4aab64cSIgor M. Liplianin 	0x0c, 0x0,
212e4aab64cSIgor M. Liplianin 	0x0d, 0x0,
213e4aab64cSIgor M. Liplianin 	0x0e, 0xd4,
214e4aab64cSIgor M. Liplianin 	0x0f, 0x30,
215e4aab64cSIgor M. Liplianin 	0x11, 0x80,
216e4aab64cSIgor M. Liplianin 	0x12, 0x03,
217e4aab64cSIgor M. Liplianin 	0x13, 0x48,
218e4aab64cSIgor M. Liplianin 	0x14, 0x84,
219e4aab64cSIgor M. Liplianin 	0x15, 0x45,
220e4aab64cSIgor M. Liplianin 	0x16, 0xb7,
221e4aab64cSIgor M. Liplianin 	0x17, 0x9c,
222e4aab64cSIgor M. Liplianin 	0x18, 0x0,
223e4aab64cSIgor M. Liplianin 	0x19, 0xa6,
224e4aab64cSIgor M. Liplianin 	0x1a, 0x88,
225e4aab64cSIgor M. Liplianin 	0x1b, 0x8f,
226e4aab64cSIgor M. Liplianin 	0x1c, 0xf0,
227e4aab64cSIgor M. Liplianin 	0x20, 0x0b,
228e4aab64cSIgor M. Liplianin 	0x21, 0x54,
229e4aab64cSIgor M. Liplianin 	0x22, 0x0,
230e4aab64cSIgor M. Liplianin 	0x23, 0x0,
231e4aab64cSIgor M. Liplianin 	0x2b, 0xff,
232e4aab64cSIgor M. Liplianin 	0x2c, 0xf7,
233e4aab64cSIgor M. Liplianin 	0x30, 0x0,
234e4aab64cSIgor M. Liplianin 	0x31, 0x1e,
235e4aab64cSIgor M. Liplianin 	0x32, 0x14,
236e4aab64cSIgor M. Liplianin 	0x33, 0x0f,
237e4aab64cSIgor M. Liplianin 	0x34, 0x09,
238e4aab64cSIgor M. Liplianin 	0x35, 0x0c,
239e4aab64cSIgor M. Liplianin 	0x36, 0x05,
240e4aab64cSIgor M. Liplianin 	0x37, 0x2f,
241e4aab64cSIgor M. Liplianin 	0x38, 0x16,
242e4aab64cSIgor M. Liplianin 	0x39, 0xbe,
243e4aab64cSIgor M. Liplianin 	0x3a, 0x0,
244e4aab64cSIgor M. Liplianin 	0x3b, 0x13,
245e4aab64cSIgor M. Liplianin 	0x3c, 0x11,
246e4aab64cSIgor M. Liplianin 	0x3d, 0x30,
247e4aab64cSIgor M. Liplianin 	0x40, 0x63,
248e4aab64cSIgor M. Liplianin 	0x41, 0x04,
249bb19a421SMalcolm Priestley 	0x42, 0x20,
250e4aab64cSIgor M. Liplianin 	0x43, 0x00,
251e4aab64cSIgor M. Liplianin 	0x44, 0x00,
252e4aab64cSIgor M. Liplianin 	0x45, 0x00,
253e4aab64cSIgor M. Liplianin 	0x46, 0x00,
254e4aab64cSIgor M. Liplianin 	0x47, 0x00,
255e4aab64cSIgor M. Liplianin 	0x4a, 0x00,
256e4aab64cSIgor M. Liplianin 	0x50, 0x10,
257e4aab64cSIgor M. Liplianin 	0x51, 0x38,
258e4aab64cSIgor M. Liplianin 	0x52, 0x21,
259e4aab64cSIgor M. Liplianin 	0x58, 0x54,
260e4aab64cSIgor M. Liplianin 	0x59, 0x86,
261e4aab64cSIgor M. Liplianin 	0x5a, 0x0,
262e4aab64cSIgor M. Liplianin 	0x5b, 0x9b,
263e4aab64cSIgor M. Liplianin 	0x5c, 0x08,
264e4aab64cSIgor M. Liplianin 	0x5d, 0x7f,
265e4aab64cSIgor M. Liplianin 	0x5e, 0x0,
266e4aab64cSIgor M. Liplianin 	0x5f, 0xff,
267e4aab64cSIgor M. Liplianin 	0x70, 0x0,
268e4aab64cSIgor M. Liplianin 	0x71, 0x0,
269e4aab64cSIgor M. Liplianin 	0x72, 0x0,
270e4aab64cSIgor M. Liplianin 	0x74, 0x0,
271e4aab64cSIgor M. Liplianin 	0x75, 0x0,
272e4aab64cSIgor M. Liplianin 	0x76, 0x0,
273e4aab64cSIgor M. Liplianin 	0x81, 0x0,
274e4aab64cSIgor M. Liplianin 	0x82, 0x3f,
275e4aab64cSIgor M. Liplianin 	0x83, 0x3f,
276e4aab64cSIgor M. Liplianin 	0x84, 0x0,
277e4aab64cSIgor M. Liplianin 	0x85, 0x0,
278e4aab64cSIgor M. Liplianin 	0x88, 0x0,
279e4aab64cSIgor M. Liplianin 	0x89, 0x0,
280e4aab64cSIgor M. Liplianin 	0x8a, 0x0,
281e4aab64cSIgor M. Liplianin 	0x8b, 0x0,
282e4aab64cSIgor M. Liplianin 	0x8c, 0x0,
283e4aab64cSIgor M. Liplianin 	0x90, 0x0,
284e4aab64cSIgor M. Liplianin 	0x91, 0x0,
285e4aab64cSIgor M. Liplianin 	0x92, 0x0,
286e4aab64cSIgor M. Liplianin 	0x93, 0x0,
287e4aab64cSIgor M. Liplianin 	0x94, 0x1c,
288e4aab64cSIgor M. Liplianin 	0x97, 0x0,
289e4aab64cSIgor M. Liplianin 	0xa0, 0x48,
290e4aab64cSIgor M. Liplianin 	0xa1, 0x0,
291e4aab64cSIgor M. Liplianin 	0xb0, 0xb8,
292e4aab64cSIgor M. Liplianin 	0xb1, 0x3a,
293e4aab64cSIgor M. Liplianin 	0xb2, 0x10,
294e4aab64cSIgor M. Liplianin 	0xb3, 0x82,
295e4aab64cSIgor M. Liplianin 	0xb4, 0x80,
296e4aab64cSIgor M. Liplianin 	0xb5, 0x82,
297e4aab64cSIgor M. Liplianin 	0xb6, 0x82,
298e4aab64cSIgor M. Liplianin 	0xb7, 0x82,
299e4aab64cSIgor M. Liplianin 	0xb8, 0x20,
300e4aab64cSIgor M. Liplianin 	0xb9, 0x0,
301e4aab64cSIgor M. Liplianin 	0xf0, 0x0,
302e4aab64cSIgor M. Liplianin 	0xf1, 0x0,
303e4aab64cSIgor M. Liplianin 	0xf2, 0xc0,
304e4aab64cSIgor M. Liplianin 	0x51, 0x36,
305e4aab64cSIgor M. Liplianin 	0x52, 0x09,
306e4aab64cSIgor M. Liplianin 	0x53, 0x94,
307e4aab64cSIgor M. Liplianin 	0x54, 0x62,
308e4aab64cSIgor M. Liplianin 	0x55, 0x29,
309e4aab64cSIgor M. Liplianin 	0x56, 0x64,
310e4aab64cSIgor M. Liplianin 	0x57, 0x2b,
311e4aab64cSIgor M. Liplianin 	0xff, 0xff,
312e4aab64cSIgor M. Liplianin };
313e4aab64cSIgor M. Liplianin 
3140df289a2SMauro Carvalho Chehab static int stv0288_set_voltage(struct dvb_frontend *fe,
3150df289a2SMauro Carvalho Chehab 			       enum fe_sec_voltage volt)
316e4aab64cSIgor M. Liplianin {
317e4aab64cSIgor M. Liplianin 	dprintk("%s: %s\n", __func__,
318e4aab64cSIgor M. Liplianin 		volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
319e4aab64cSIgor M. Liplianin 		volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
320e4aab64cSIgor M. Liplianin 
321e4aab64cSIgor M. Liplianin 	return 0;
322e4aab64cSIgor M. Liplianin }
323e4aab64cSIgor M. Liplianin 
324e4aab64cSIgor M. Liplianin static int stv0288_init(struct dvb_frontend *fe)
325e4aab64cSIgor M. Liplianin {
326e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
327e4aab64cSIgor M. Liplianin 	int i;
328de9be0eaSIgor M. Liplianin 	u8 reg;
329de9be0eaSIgor M. Liplianin 	u8 val;
330e4aab64cSIgor M. Liplianin 
331e4aab64cSIgor M. Liplianin 	dprintk("stv0288: init chip\n");
332e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x41, 0x04);
333e4aab64cSIgor M. Liplianin 	msleep(50);
334e4aab64cSIgor M. Liplianin 
335de9be0eaSIgor M. Liplianin 	/* we have default inittab */
336de9be0eaSIgor M. Liplianin 	if (state->config->inittab == NULL) {
337e4aab64cSIgor M. Liplianin 		for (i = 0; !(stv0288_inittab[i] == 0xff &&
338e4aab64cSIgor M. Liplianin 				stv0288_inittab[i + 1] == 0xff); i += 2)
339e4aab64cSIgor M. Liplianin 			stv0288_writeregI(state, stv0288_inittab[i],
340e4aab64cSIgor M. Liplianin 					stv0288_inittab[i + 1]);
341de9be0eaSIgor M. Liplianin 	} else {
342de9be0eaSIgor M. Liplianin 		for (i = 0; ; i += 2)  {
343de9be0eaSIgor M. Liplianin 			reg = state->config->inittab[i];
344de9be0eaSIgor M. Liplianin 			val = state->config->inittab[i+1];
345de9be0eaSIgor M. Liplianin 			if (reg == 0xff && val == 0xff)
346de9be0eaSIgor M. Liplianin 				break;
347de9be0eaSIgor M. Liplianin 			stv0288_writeregI(state, reg, val);
348de9be0eaSIgor M. Liplianin 		}
349de9be0eaSIgor M. Liplianin 	}
350e4aab64cSIgor M. Liplianin 	return 0;
351e4aab64cSIgor M. Liplianin }
352e4aab64cSIgor M. Liplianin 
3530df289a2SMauro Carvalho Chehab static int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status)
354e4aab64cSIgor M. Liplianin {
355e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
356e4aab64cSIgor M. Liplianin 
357e4aab64cSIgor M. Liplianin 	u8 sync = stv0288_readreg(state, 0x24);
358e4aab64cSIgor M. Liplianin 	if (sync == 255)
359e4aab64cSIgor M. Liplianin 		sync = 0;
360e4aab64cSIgor M. Liplianin 
361e4aab64cSIgor M. Liplianin 	dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync);
362e4aab64cSIgor M. Liplianin 
363e4aab64cSIgor M. Liplianin 	*status = 0;
364b50b3a1aSMalcolm Priestley 	if (sync & 0x80)
365b50b3a1aSMalcolm Priestley 		*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
366b50b3a1aSMalcolm Priestley 	if (sync & 0x10)
367b50b3a1aSMalcolm Priestley 		*status |= FE_HAS_VITERBI;
368b50b3a1aSMalcolm Priestley 	if (sync & 0x08) {
369e4aab64cSIgor M. Liplianin 		*status |= FE_HAS_LOCK;
370e4aab64cSIgor M. Liplianin 		dprintk("stv0288 has locked\n");
371e4aab64cSIgor M. Liplianin 	}
372e4aab64cSIgor M. Liplianin 
373e4aab64cSIgor M. Liplianin 	return 0;
374e4aab64cSIgor M. Liplianin }
375e4aab64cSIgor M. Liplianin 
376e4aab64cSIgor M. Liplianin static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber)
377e4aab64cSIgor M. Liplianin {
378e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
379e4aab64cSIgor M. Liplianin 
380e4aab64cSIgor M. Liplianin 	if (state->errmode != STATUS_BER)
381e4aab64cSIgor M. Liplianin 		return 0;
382e4aab64cSIgor M. Liplianin 	*ber = (stv0288_readreg(state, 0x26) << 8) |
383e4aab64cSIgor M. Liplianin 					stv0288_readreg(state, 0x27);
384e4aab64cSIgor M. Liplianin 	dprintk("stv0288_read_ber %d\n", *ber);
385e4aab64cSIgor M. Liplianin 
386e4aab64cSIgor M. Liplianin 	return 0;
387e4aab64cSIgor M. Liplianin }
388e4aab64cSIgor M. Liplianin 
389e4aab64cSIgor M. Liplianin 
390e4aab64cSIgor M. Liplianin static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
391e4aab64cSIgor M. Liplianin {
392e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
393e4aab64cSIgor M. Liplianin 
394e4aab64cSIgor M. Liplianin 	s32 signal =  0xffff - ((stv0288_readreg(state, 0x10) << 8));
395e4aab64cSIgor M. Liplianin 
396e4aab64cSIgor M. Liplianin 
397e4aab64cSIgor M. Liplianin 	signal = signal * 5 / 4;
398e4aab64cSIgor M. Liplianin 	*strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
399e4aab64cSIgor M. Liplianin 	dprintk("stv0288_read_signal_strength %d\n", *strength);
400e4aab64cSIgor M. Liplianin 
401e4aab64cSIgor M. Liplianin 	return 0;
402e4aab64cSIgor M. Liplianin }
403e4aab64cSIgor M. Liplianin static int stv0288_sleep(struct dvb_frontend *fe)
404e4aab64cSIgor M. Liplianin {
405e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
406e4aab64cSIgor M. Liplianin 
407e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x41, 0x84);
408e4aab64cSIgor M. Liplianin 	state->initialised = 0;
409e4aab64cSIgor M. Liplianin 
410e4aab64cSIgor M. Liplianin 	return 0;
411e4aab64cSIgor M. Liplianin }
412e4aab64cSIgor M. Liplianin static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr)
413e4aab64cSIgor M. Liplianin {
414e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
415e4aab64cSIgor M. Liplianin 
416e4aab64cSIgor M. Liplianin 	s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8)
417e4aab64cSIgor M. Liplianin 			   | stv0288_readreg(state, 0x2e));
418e4aab64cSIgor M. Liplianin 	xsnr = 3 * (xsnr - 0xa100);
419e4aab64cSIgor M. Liplianin 	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
420e4aab64cSIgor M. Liplianin 	dprintk("stv0288_read_snr %d\n", *snr);
421e4aab64cSIgor M. Liplianin 
422e4aab64cSIgor M. Liplianin 	return 0;
423e4aab64cSIgor M. Liplianin }
424e4aab64cSIgor M. Liplianin 
425e4aab64cSIgor M. Liplianin static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
426e4aab64cSIgor M. Liplianin {
427e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
428e4aab64cSIgor M. Liplianin 
429e4aab64cSIgor M. Liplianin 	if (state->errmode != STATUS_BER)
430e4aab64cSIgor M. Liplianin 		return 0;
431e4aab64cSIgor M. Liplianin 	*ucblocks = (stv0288_readreg(state, 0x26) << 8) |
432e4aab64cSIgor M. Liplianin 					stv0288_readreg(state, 0x27);
433e4aab64cSIgor M. Liplianin 	dprintk("stv0288_read_ber %d\n", *ucblocks);
434e4aab64cSIgor M. Liplianin 
435e4aab64cSIgor M. Liplianin 	return 0;
436e4aab64cSIgor M. Liplianin }
437e4aab64cSIgor M. Liplianin 
4385c6b4e2bSMauro Carvalho Chehab static int stv0288_set_frontend(struct dvb_frontend *fe)
439e4aab64cSIgor M. Liplianin {
440e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
441e4aab64cSIgor M. Liplianin 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
442e4aab64cSIgor M. Liplianin 
443e4aab64cSIgor M. Liplianin 	char tm;
444e4aab64cSIgor M. Liplianin 	unsigned char tda[3];
44559152b1cStvboxspy 	u8 reg, time_out = 0;
446e4aab64cSIgor M. Liplianin 
447e4aab64cSIgor M. Liplianin 	dprintk("%s : FE_SET_FRONTEND\n", __func__);
448e4aab64cSIgor M. Liplianin 
449e4aab64cSIgor M. Liplianin 	if (c->delivery_system != SYS_DVBS) {
4504bd69e7bSMauro Carvalho Chehab 		dprintk("%s: unsupported delivery system selected (%d)\n",
451e4aab64cSIgor M. Liplianin 			__func__, c->delivery_system);
452e4aab64cSIgor M. Liplianin 		return -EOPNOTSUPP;
453e4aab64cSIgor M. Liplianin 	}
454e4aab64cSIgor M. Liplianin 
455e4aab64cSIgor M. Liplianin 	if (state->config->set_ts_params)
456e4aab64cSIgor M. Liplianin 		state->config->set_ts_params(fe, 0);
457e4aab64cSIgor M. Liplianin 
458e4aab64cSIgor M. Liplianin 	/* only frequency & symbol_rate are used for tuner*/
459e4aab64cSIgor M. Liplianin 	if (fe->ops.tuner_ops.set_params) {
46014d24d14SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
461e4aab64cSIgor M. Liplianin 		if (fe->ops.i2c_gate_ctrl)
462e4aab64cSIgor M. Liplianin 			fe->ops.i2c_gate_ctrl(fe, 0);
463e4aab64cSIgor M. Liplianin 	}
464e4aab64cSIgor M. Liplianin 
465e4aab64cSIgor M. Liplianin 	udelay(10);
466e4aab64cSIgor M. Liplianin 	stv0288_set_symbolrate(fe, c->symbol_rate);
467e4aab64cSIgor M. Liplianin 	/* Carrier lock control register */
468e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x15, 0xc5);
469e4aab64cSIgor M. Liplianin 
470e4aab64cSIgor M. Liplianin 	tda[2] = 0x0; /* CFRL */
47159152b1cStvboxspy 	for (tm = -9; tm < 7;) {
472e4aab64cSIgor M. Liplianin 		/* Viterbi status */
47359152b1cStvboxspy 		reg = stv0288_readreg(state, 0x24);
47459152b1cStvboxspy 		if (reg & 0x8)
475e4aab64cSIgor M. Liplianin 				break;
47659152b1cStvboxspy 		if (reg & 0x80) {
47759152b1cStvboxspy 			time_out++;
47859152b1cStvboxspy 			if (time_out > 10)
47959152b1cStvboxspy 				break;
480e4aab64cSIgor M. Liplianin 			tda[2] += 40;
481e4aab64cSIgor M. Liplianin 			if (tda[2] < 40)
482e4aab64cSIgor M. Liplianin 				tm++;
48359152b1cStvboxspy 		} else {
48459152b1cStvboxspy 			tm++;
48559152b1cStvboxspy 			tda[2] = 0;
48659152b1cStvboxspy 			time_out = 0;
48759152b1cStvboxspy 		}
488e4aab64cSIgor M. Liplianin 		tda[1] = (unsigned char)tm;
489e4aab64cSIgor M. Liplianin 		stv0288_writeregI(state, 0x2b, tda[1]);
490e4aab64cSIgor M. Liplianin 		stv0288_writeregI(state, 0x2c, tda[2]);
49177768e4bSMalcolm Priestley 		msleep(30);
492e4aab64cSIgor M. Liplianin 	}
493e4aab64cSIgor M. Liplianin 	state->tuner_frequency = c->frequency;
494e4aab64cSIgor M. Liplianin 	state->fec_inner = FEC_AUTO;
495e4aab64cSIgor M. Liplianin 	state->symbol_rate = c->symbol_rate;
496e4aab64cSIgor M. Liplianin 
497e4aab64cSIgor M. Liplianin 	return 0;
498e4aab64cSIgor M. Liplianin }
499e4aab64cSIgor M. Liplianin 
500e4aab64cSIgor M. Liplianin static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
501e4aab64cSIgor M. Liplianin {
502e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
503e4aab64cSIgor M. Liplianin 
504e4aab64cSIgor M. Liplianin 	if (enable)
505e4aab64cSIgor M. Liplianin 		stv0288_writeregI(state, 0x01, 0xb5);
506e4aab64cSIgor M. Liplianin 	else
507e4aab64cSIgor M. Liplianin 		stv0288_writeregI(state, 0x01, 0x35);
508e4aab64cSIgor M. Liplianin 
509e4aab64cSIgor M. Liplianin 	udelay(1);
510e4aab64cSIgor M. Liplianin 
511e4aab64cSIgor M. Liplianin 	return 0;
512e4aab64cSIgor M. Liplianin }
513e4aab64cSIgor M. Liplianin 
514e4aab64cSIgor M. Liplianin static void stv0288_release(struct dvb_frontend *fe)
515e4aab64cSIgor M. Liplianin {
516e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = fe->demodulator_priv;
517e4aab64cSIgor M. Liplianin 	kfree(state);
518e4aab64cSIgor M. Liplianin }
519e4aab64cSIgor M. Liplianin 
520bd336e63SMax Kellermann static const struct dvb_frontend_ops stv0288_ops = {
5215c6b4e2bSMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
522e4aab64cSIgor M. Liplianin 	.info = {
523e4aab64cSIgor M. Liplianin 		.name			= "ST STV0288 DVB-S",
524f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  950 * MHz,
525f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 2150 * MHz,
526f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	=    1 * MHz,
527e4aab64cSIgor M. Liplianin 		.symbol_rate_min	= 1000000,
528e4aab64cSIgor M. Liplianin 		.symbol_rate_max	= 45000000,
529e4aab64cSIgor M. Liplianin 		.symbol_rate_tolerance	= 500,	/* ppm */
530e4aab64cSIgor M. Liplianin 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
531e4aab64cSIgor M. Liplianin 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
532e4aab64cSIgor M. Liplianin 		      FE_CAN_QPSK |
533e4aab64cSIgor M. Liplianin 		      FE_CAN_FEC_AUTO
534e4aab64cSIgor M. Liplianin 	},
535e4aab64cSIgor M. Liplianin 
536e4aab64cSIgor M. Liplianin 	.release = stv0288_release,
537e4aab64cSIgor M. Liplianin 	.init = stv0288_init,
538e4aab64cSIgor M. Liplianin 	.sleep = stv0288_sleep,
539e4aab64cSIgor M. Liplianin 	.write = stv0288_write,
540e4aab64cSIgor M. Liplianin 	.i2c_gate_ctrl = stv0288_i2c_gate_ctrl,
541e4aab64cSIgor M. Liplianin 	.read_status = stv0288_read_status,
542e4aab64cSIgor M. Liplianin 	.read_ber = stv0288_read_ber,
543e4aab64cSIgor M. Liplianin 	.read_signal_strength = stv0288_read_signal_strength,
544e4aab64cSIgor M. Liplianin 	.read_snr = stv0288_read_snr,
545e4aab64cSIgor M. Liplianin 	.read_ucblocks = stv0288_read_ucblocks,
546e4aab64cSIgor M. Liplianin 	.diseqc_send_master_cmd = stv0288_send_diseqc_msg,
547e4aab64cSIgor M. Liplianin 	.diseqc_send_burst = stv0288_send_diseqc_burst,
548e4aab64cSIgor M. Liplianin 	.set_tone = stv0288_set_tone,
549e4aab64cSIgor M. Liplianin 	.set_voltage = stv0288_set_voltage,
550e4aab64cSIgor M. Liplianin 
5515c6b4e2bSMauro Carvalho Chehab 	.set_frontend = stv0288_set_frontend,
552e4aab64cSIgor M. Liplianin };
553e4aab64cSIgor M. Liplianin 
554e4aab64cSIgor M. Liplianin struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
555e4aab64cSIgor M. Liplianin 				    struct i2c_adapter *i2c)
556e4aab64cSIgor M. Liplianin {
557e4aab64cSIgor M. Liplianin 	struct stv0288_state *state = NULL;
558e4aab64cSIgor M. Liplianin 	int id;
559e4aab64cSIgor M. Liplianin 
560e4aab64cSIgor M. Liplianin 	/* allocate memory for the internal state */
561084e24acSMatthias Schwarzott 	state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL);
562e4aab64cSIgor M. Liplianin 	if (state == NULL)
563e4aab64cSIgor M. Liplianin 		goto error;
564e4aab64cSIgor M. Liplianin 
565e4aab64cSIgor M. Liplianin 	/* setup the state */
566e4aab64cSIgor M. Liplianin 	state->config = config;
567e4aab64cSIgor M. Liplianin 	state->i2c = i2c;
568e4aab64cSIgor M. Liplianin 	state->initialised = 0;
569e4aab64cSIgor M. Liplianin 	state->tuner_frequency = 0;
570e4aab64cSIgor M. Liplianin 	state->symbol_rate = 0;
571e4aab64cSIgor M. Liplianin 	state->fec_inner = 0;
572e4aab64cSIgor M. Liplianin 	state->errmode = STATUS_BER;
573e4aab64cSIgor M. Liplianin 
574e4aab64cSIgor M. Liplianin 	stv0288_writeregI(state, 0x41, 0x04);
575e4aab64cSIgor M. Liplianin 	msleep(200);
576e4aab64cSIgor M. Liplianin 	id = stv0288_readreg(state, 0x00);
577e4aab64cSIgor M. Liplianin 	dprintk("stv0288 id %x\n", id);
578e4aab64cSIgor M. Liplianin 
579e4aab64cSIgor M. Liplianin 	/* register 0x00 contains 0x11 for STV0288  */
580e4aab64cSIgor M. Liplianin 	if (id != 0x11)
581e4aab64cSIgor M. Liplianin 		goto error;
582e4aab64cSIgor M. Liplianin 
583e4aab64cSIgor M. Liplianin 	/* create dvb_frontend */
584e4aab64cSIgor M. Liplianin 	memcpy(&state->frontend.ops, &stv0288_ops,
585e4aab64cSIgor M. Liplianin 			sizeof(struct dvb_frontend_ops));
586e4aab64cSIgor M. Liplianin 	state->frontend.demodulator_priv = state;
587e4aab64cSIgor M. Liplianin 	return &state->frontend;
588e4aab64cSIgor M. Liplianin 
589e4aab64cSIgor M. Liplianin error:
590e4aab64cSIgor M. Liplianin 	kfree(state);
591e4aab64cSIgor M. Liplianin 
592e4aab64cSIgor M. Liplianin 	return NULL;
593e4aab64cSIgor M. Liplianin }
594e4aab64cSIgor M. Liplianin EXPORT_SYMBOL(stv0288_attach);
595e4aab64cSIgor M. Liplianin 
596e4aab64cSIgor M. Liplianin module_param(debug_legacy_dish_switch, int, 0444);
597e4aab64cSIgor M. Liplianin MODULE_PARM_DESC(debug_legacy_dish_switch,
598e4aab64cSIgor M. Liplianin 		"Enable timing analysis for Dish Network legacy switches");
599e4aab64cSIgor M. Liplianin 
600e4aab64cSIgor M. Liplianin module_param(debug, int, 0644);
601e4aab64cSIgor M. Liplianin MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
602e4aab64cSIgor M. Liplianin 
603e4aab64cSIgor M. Liplianin MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver");
604e4aab64cSIgor M. Liplianin MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin");
605e4aab64cSIgor M. Liplianin MODULE_LICENSE("GPL");
606e4aab64cSIgor M. Liplianin 
607