19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab 	Driver for M88RS2000 demodulator and tuner
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab 	Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
59a0bf528SMauro Carvalho Chehab 	Beta Driver
69a0bf528SMauro Carvalho Chehab 
79a0bf528SMauro Carvalho Chehab 	Include various calculation code from DS3000 driver.
89a0bf528SMauro Carvalho Chehab 	Copyright (C) 2009 Konstantin Dimitrov.
99a0bf528SMauro Carvalho Chehab 
109a0bf528SMauro Carvalho Chehab 	This program is free software; you can redistribute it and/or modify
119a0bf528SMauro Carvalho Chehab 	it under the terms of the GNU General Public License as published by
129a0bf528SMauro Carvalho Chehab 	the Free Software Foundation; either version 2 of the License, or
139a0bf528SMauro Carvalho Chehab 	(at your option) any later version.
149a0bf528SMauro Carvalho Chehab 
159a0bf528SMauro Carvalho Chehab 	This program is distributed in the hope that it will be useful,
169a0bf528SMauro Carvalho Chehab 	but WITHOUT ANY WARRANTY; without even the implied warranty of
179a0bf528SMauro Carvalho Chehab 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
189a0bf528SMauro Carvalho Chehab 	GNU General Public License for more details.
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab 	You should have received a copy of the GNU General Public License
219a0bf528SMauro Carvalho Chehab 	along with this program; if not, write to the Free Software
229a0bf528SMauro Carvalho Chehab 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
239a0bf528SMauro Carvalho Chehab 
249a0bf528SMauro Carvalho Chehab */
259a0bf528SMauro Carvalho Chehab #include <linux/init.h>
269a0bf528SMauro Carvalho Chehab #include <linux/module.h>
279a0bf528SMauro Carvalho Chehab #include <linux/device.h>
289a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
299a0bf528SMauro Carvalho Chehab #include <linux/string.h>
309a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
319a0bf528SMauro Carvalho Chehab #include <linux/types.h>
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
359a0bf528SMauro Carvalho Chehab #include "m88rs2000.h"
369a0bf528SMauro Carvalho Chehab 
379a0bf528SMauro Carvalho Chehab struct m88rs2000_state {
389a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
399a0bf528SMauro Carvalho Chehab 	const struct m88rs2000_config *config;
409a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
419a0bf528SMauro Carvalho Chehab 	u8 no_lock_count;
429a0bf528SMauro Carvalho Chehab 	u32 tuner_frequency;
439a0bf528SMauro Carvalho Chehab 	u32 symbol_rate;
449a0bf528SMauro Carvalho Chehab 	fe_code_rate_t fec_inner;
459a0bf528SMauro Carvalho Chehab 	u8 tuner_level;
469a0bf528SMauro Carvalho Chehab 	int errmode;
479a0bf528SMauro Carvalho Chehab };
489a0bf528SMauro Carvalho Chehab 
499a0bf528SMauro Carvalho Chehab static int m88rs2000_debug;
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab module_param_named(debug, m88rs2000_debug, int, 0644);
529a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab #define dprintk(level, args...) do { \
559a0bf528SMauro Carvalho Chehab 	if (level & m88rs2000_debug) \
569a0bf528SMauro Carvalho Chehab 		printk(KERN_DEBUG "m88rs2000-fe: " args); \
579a0bf528SMauro Carvalho Chehab } while (0)
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab #define deb_info(args...)  dprintk(0x01, args)
609a0bf528SMauro Carvalho Chehab #define info(format, arg...) \
619a0bf528SMauro Carvalho Chehab 	printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
629a0bf528SMauro Carvalho Chehab 
63b858c331SIgor M. Liplianin static int m88rs2000_writereg(struct m88rs2000_state *state,
649a0bf528SMauro Carvalho Chehab 	u8 reg, u8 data)
659a0bf528SMauro Carvalho Chehab {
669a0bf528SMauro Carvalho Chehab 	int ret;
679a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
689a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
69b858c331SIgor M. Liplianin 		.addr = state->config->demod_addr,
709a0bf528SMauro Carvalho Chehab 		.flags = 0,
719a0bf528SMauro Carvalho Chehab 		.buf = buf,
729a0bf528SMauro Carvalho Chehab 		.len = 2
739a0bf528SMauro Carvalho Chehab 	};
749a0bf528SMauro Carvalho Chehab 
759a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
769a0bf528SMauro Carvalho Chehab 
779a0bf528SMauro Carvalho Chehab 	if (ret != 1)
789a0bf528SMauro Carvalho Chehab 		deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
799a0bf528SMauro Carvalho Chehab 			"ret == %i)\n", __func__, reg, data, ret);
809a0bf528SMauro Carvalho Chehab 
819a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
829a0bf528SMauro Carvalho Chehab }
839a0bf528SMauro Carvalho Chehab 
84b858c331SIgor M. Liplianin static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg)
859a0bf528SMauro Carvalho Chehab {
869a0bf528SMauro Carvalho Chehab 	int ret;
879a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
889a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
89b858c331SIgor M. Liplianin 
909a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
919a0bf528SMauro Carvalho Chehab 		{
92b858c331SIgor M. Liplianin 			.addr = state->config->demod_addr,
939a0bf528SMauro Carvalho Chehab 			.flags = 0,
949a0bf528SMauro Carvalho Chehab 			.buf = b0,
959a0bf528SMauro Carvalho Chehab 			.len = 1
969a0bf528SMauro Carvalho Chehab 		}, {
97b858c331SIgor M. Liplianin 			.addr = state->config->demod_addr,
989a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
999a0bf528SMauro Carvalho Chehab 			.buf = b1,
1009a0bf528SMauro Carvalho Chehab 			.len = 1
1019a0bf528SMauro Carvalho Chehab 		}
1029a0bf528SMauro Carvalho Chehab 	};
1039a0bf528SMauro Carvalho Chehab 
1049a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
1059a0bf528SMauro Carvalho Chehab 
1069a0bf528SMauro Carvalho Chehab 	if (ret != 2)
1079a0bf528SMauro Carvalho Chehab 		deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
1089a0bf528SMauro Carvalho Chehab 				__func__, reg, ret);
1099a0bf528SMauro Carvalho Chehab 
1109a0bf528SMauro Carvalho Chehab 	return b1[0];
1119a0bf528SMauro Carvalho Chehab }
1129a0bf528SMauro Carvalho Chehab 
11306af15d1SMalcolm Priestley static u32 m88rs2000_get_mclk(struct dvb_frontend *fe)
11406af15d1SMalcolm Priestley {
11506af15d1SMalcolm Priestley 	struct m88rs2000_state *state = fe->demodulator_priv;
11606af15d1SMalcolm Priestley 	u32 mclk;
11706af15d1SMalcolm Priestley 	u8 reg;
11806af15d1SMalcolm Priestley 	/* Must not be 0x00 or 0xff */
11906af15d1SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x86);
12006af15d1SMalcolm Priestley 	if (!reg || reg == 0xff)
12106af15d1SMalcolm Priestley 		return 0;
12206af15d1SMalcolm Priestley 
12306af15d1SMalcolm Priestley 	reg /= 2;
12406af15d1SMalcolm Priestley 	reg += 1;
12506af15d1SMalcolm Priestley 
12606af15d1SMalcolm Priestley 	mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28;
12706af15d1SMalcolm Priestley 
12806af15d1SMalcolm Priestley 	return mclk;
12906af15d1SMalcolm Priestley }
13006af15d1SMalcolm Priestley 
13106af15d1SMalcolm Priestley static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset)
13206af15d1SMalcolm Priestley {
13306af15d1SMalcolm Priestley 	struct m88rs2000_state *state = fe->demodulator_priv;
13406af15d1SMalcolm Priestley 	u32 mclk;
13506af15d1SMalcolm Priestley 	s32 tmp;
13606af15d1SMalcolm Priestley 	u8 reg;
13706af15d1SMalcolm Priestley 	int ret;
13806af15d1SMalcolm Priestley 
13906af15d1SMalcolm Priestley 	mclk = m88rs2000_get_mclk(fe);
14006af15d1SMalcolm Priestley 	if (!mclk)
14106af15d1SMalcolm Priestley 		return -EINVAL;
14206af15d1SMalcolm Priestley 
14306af15d1SMalcolm Priestley 	tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk;
14406af15d1SMalcolm Priestley 	if (tmp < 0)
14506af15d1SMalcolm Priestley 		tmp += 4096;
14606af15d1SMalcolm Priestley 
14706af15d1SMalcolm Priestley 	/* Carrier Offset */
14806af15d1SMalcolm Priestley 	ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4));
14906af15d1SMalcolm Priestley 
15006af15d1SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x9d);
15106af15d1SMalcolm Priestley 	reg &= 0xf;
15206af15d1SMalcolm Priestley 	reg |= (u8)(tmp & 0xf) << 4;
15306af15d1SMalcolm Priestley 
15406af15d1SMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0x9d, reg);
15506af15d1SMalcolm Priestley 
15606af15d1SMalcolm Priestley 	return ret;
15706af15d1SMalcolm Priestley }
15806af15d1SMalcolm Priestley 
1599a0bf528SMauro Carvalho Chehab static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
1609a0bf528SMauro Carvalho Chehab {
1619a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
1629a0bf528SMauro Carvalho Chehab 	int ret;
163dd4491dfSMalcolm Priestley 	u64 temp;
164dd4491dfSMalcolm Priestley 	u32 mclk;
1659a0bf528SMauro Carvalho Chehab 	u8 b[3];
1669a0bf528SMauro Carvalho Chehab 
1679a0bf528SMauro Carvalho Chehab 	if ((srate < 1000000) || (srate > 45000000))
1689a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1699a0bf528SMauro Carvalho Chehab 
170dd4491dfSMalcolm Priestley 	mclk = m88rs2000_get_mclk(fe);
171dd4491dfSMalcolm Priestley 	if (!mclk)
172dd4491dfSMalcolm Priestley 		return -EINVAL;
173dd4491dfSMalcolm Priestley 
1749a0bf528SMauro Carvalho Chehab 	temp = srate / 1000;
175dd4491dfSMalcolm Priestley 	temp *= 1 << 24;
176dd4491dfSMalcolm Priestley 
177dd4491dfSMalcolm Priestley 	do_div(temp, mclk);
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	b[0] = (u8) (temp >> 16) & 0xff;
1809a0bf528SMauro Carvalho Chehab 	b[1] = (u8) (temp >> 8) & 0xff;
1819a0bf528SMauro Carvalho Chehab 	b[2] = (u8) temp & 0xff;
182dd4491dfSMalcolm Priestley 
183b858c331SIgor M. Liplianin 	ret = m88rs2000_writereg(state, 0x93, b[2]);
184b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x94, b[1]);
185b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x95, b[0]);
1869a0bf528SMauro Carvalho Chehab 
187dd4491dfSMalcolm Priestley 	if (srate > 10000000)
188dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa0, 0x20);
189dd4491dfSMalcolm Priestley 	else
190dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa0, 0x60);
191dd4491dfSMalcolm Priestley 
192dd4491dfSMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0xa1, 0xe0);
193dd4491dfSMalcolm Priestley 
194dd4491dfSMalcolm Priestley 	if (srate > 12000000)
195dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x20);
196dd4491dfSMalcolm Priestley 	else if (srate > 2800000)
197dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x98);
198dd4491dfSMalcolm Priestley 	else
199dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x90);
200dd4491dfSMalcolm Priestley 
2019a0bf528SMauro Carvalho Chehab 	deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
2029a0bf528SMauro Carvalho Chehab 	return ret;
2039a0bf528SMauro Carvalho Chehab }
2049a0bf528SMauro Carvalho Chehab 
2059a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
2069a0bf528SMauro Carvalho Chehab 				    struct dvb_diseqc_master_cmd *m)
2079a0bf528SMauro Carvalho Chehab {
2089a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
2099a0bf528SMauro Carvalho Chehab 
2109a0bf528SMauro Carvalho Chehab 	int i;
2119a0bf528SMauro Carvalho Chehab 	u8 reg;
2129a0bf528SMauro Carvalho Chehab 	deb_info("%s\n", __func__);
213b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
214b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb2);
2159a0bf528SMauro Carvalho Chehab 	reg &= 0x3f;
216b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg);
2179a0bf528SMauro Carvalho Chehab 	for (i = 0; i <  m->msg_len; i++)
218b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0xb3 + i, m->msg[i]);
2199a0bf528SMauro Carvalho Chehab 
220b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb1);
2219a0bf528SMauro Carvalho Chehab 	reg &= 0x87;
2229a0bf528SMauro Carvalho Chehab 	reg |= ((m->msg_len - 1) << 3) | 0x07;
2239a0bf528SMauro Carvalho Chehab 	reg &= 0x7f;
224b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg);
2259a0bf528SMauro Carvalho Chehab 
2269a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 15; i++) {
227b858c331SIgor M. Liplianin 		if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0)
2289a0bf528SMauro Carvalho Chehab 			break;
2299a0bf528SMauro Carvalho Chehab 		msleep(20);
2309a0bf528SMauro Carvalho Chehab 	}
2319a0bf528SMauro Carvalho Chehab 
232b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb1);
2339a0bf528SMauro Carvalho Chehab 	if ((reg & 0x40) > 0x0) {
2349a0bf528SMauro Carvalho Chehab 		reg &= 0x7f;
2359a0bf528SMauro Carvalho Chehab 		reg |= 0x40;
236b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0xb1, reg);
2379a0bf528SMauro Carvalho Chehab 	}
2389a0bf528SMauro Carvalho Chehab 
239b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb2);
2409a0bf528SMauro Carvalho Chehab 	reg &= 0x3f;
2419a0bf528SMauro Carvalho Chehab 	reg |= 0x80;
242b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg);
243b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2449a0bf528SMauro Carvalho Chehab 
2459a0bf528SMauro Carvalho Chehab 
2469a0bf528SMauro Carvalho Chehab 	return 0;
2479a0bf528SMauro Carvalho Chehab }
2489a0bf528SMauro Carvalho Chehab 
2499a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
2509a0bf528SMauro Carvalho Chehab 						fe_sec_mini_cmd_t burst)
2519a0bf528SMauro Carvalho Chehab {
2529a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
2539a0bf528SMauro Carvalho Chehab 	u8 reg0, reg1;
2549a0bf528SMauro Carvalho Chehab 	deb_info("%s\n", __func__);
255b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
2569a0bf528SMauro Carvalho Chehab 	msleep(50);
257b858c331SIgor M. Liplianin 	reg0 = m88rs2000_readreg(state, 0xb1);
258b858c331SIgor M. Liplianin 	reg1 = m88rs2000_readreg(state, 0xb2);
2599a0bf528SMauro Carvalho Chehab 	/* TODO complete this section */
260b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg1);
261b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg0);
262b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2639a0bf528SMauro Carvalho Chehab 
2649a0bf528SMauro Carvalho Chehab 	return 0;
2659a0bf528SMauro Carvalho Chehab }
2669a0bf528SMauro Carvalho Chehab 
2679a0bf528SMauro Carvalho Chehab static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
2689a0bf528SMauro Carvalho Chehab {
2699a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
2709a0bf528SMauro Carvalho Chehab 	u8 reg0, reg1;
271b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
272b858c331SIgor M. Liplianin 	reg0 = m88rs2000_readreg(state, 0xb1);
273b858c331SIgor M. Liplianin 	reg1 = m88rs2000_readreg(state, 0xb2);
2749a0bf528SMauro Carvalho Chehab 
2759a0bf528SMauro Carvalho Chehab 	reg1 &= 0x3f;
2769a0bf528SMauro Carvalho Chehab 
2779a0bf528SMauro Carvalho Chehab 	switch (tone) {
2789a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
2799a0bf528SMauro Carvalho Chehab 		reg0 |= 0x4;
2809a0bf528SMauro Carvalho Chehab 		reg0 &= 0xbc;
2819a0bf528SMauro Carvalho Chehab 		break;
2829a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
2839a0bf528SMauro Carvalho Chehab 		reg1 |= 0x80;
2849a0bf528SMauro Carvalho Chehab 		break;
2859a0bf528SMauro Carvalho Chehab 	default:
2869a0bf528SMauro Carvalho Chehab 		break;
2879a0bf528SMauro Carvalho Chehab 	}
288b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg1);
289b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg0);
290b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2919a0bf528SMauro Carvalho Chehab 	return 0;
2929a0bf528SMauro Carvalho Chehab }
2939a0bf528SMauro Carvalho Chehab 
2949a0bf528SMauro Carvalho Chehab struct inittab {
2959a0bf528SMauro Carvalho Chehab 	u8 cmd;
2969a0bf528SMauro Carvalho Chehab 	u8 reg;
2979a0bf528SMauro Carvalho Chehab 	u8 val;
2989a0bf528SMauro Carvalho Chehab };
2999a0bf528SMauro Carvalho Chehab 
3009a0bf528SMauro Carvalho Chehab struct inittab m88rs2000_setup[] = {
3019a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
3029a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3039a0bf528SMauro Carvalho Chehab 	{WRITE_DELAY, 0x19, 0x00},
3049a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
3059a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3069a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0xc1},
3079a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0x81},
3089a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x86, 0xc6},
3099a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
3109a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf0, 0x22},
3119a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf1, 0xbf},
3129a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb0, 0x45},
3139a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
3149a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3159a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3169a0bf528SMauro Carvalho Chehab };
3179a0bf528SMauro Carvalho Chehab 
3189a0bf528SMauro Carvalho Chehab struct inittab m88rs2000_shutdown[] = {
3199a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
3209a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb0, 0x00},
3219a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf1, 0x89},
3229a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3239a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3249a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0x81},
3259a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3269a0bf528SMauro Carvalho Chehab };
3279a0bf528SMauro Carvalho Chehab 
3289a0bf528SMauro Carvalho Chehab struct inittab fe_reset[] = {
3299a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3309a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x20, 0x81},
3319a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x21, 0x80},
3329a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x10, 0x33},
3339a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x11, 0x44},
3349a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x12, 0x07},
3359a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x18, 0x20},
3369a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x28, 0x04},
3379a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x29, 0x8e},
3389a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3b, 0xff},
3399a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x32, 0x10},
3409a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x33, 0x02},
3419a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x34, 0x30},
3429a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x35, 0xff},
3439a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x38, 0x50},
3449a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x39, 0x68},
3459a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3c, 0x7f},
3469a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3d, 0x0f},
3479a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x45, 0x20},
3489a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x46, 0x24},
3499a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x47, 0x7c},
3509a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x48, 0x16},
3519a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x49, 0x04},
3529a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4a, 0x01},
3539a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4b, 0x78},
3549a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0X4d, 0xd2},
3559a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4e, 0x6d},
3569a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x50, 0x30},
3579a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x51, 0x30},
3589a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x54, 0x7b},
3599a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x56, 0x09},
3609a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x58, 0x59},
3619a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x59, 0x37},
3629a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x63, 0xfa},
3639a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3649a0bf528SMauro Carvalho Chehab };
3659a0bf528SMauro Carvalho Chehab 
3669a0bf528SMauro Carvalho Chehab struct inittab fe_trigger[] = {
3679a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x97, 0x04},
3689a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x99, 0x77},
3699a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9b, 0x64},
3709a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9e, 0x00},
3719a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9f, 0xf8},
3729a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x98, 0xff},
3739a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xc0, 0x0f},
3749a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x89, 0x01},
3759a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
3769a0bf528SMauro Carvalho Chehab 	{WRITE_DELAY, 0x0a, 0x00},
3779a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3789a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
3799a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3809a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3819a0bf528SMauro Carvalho Chehab };
3829a0bf528SMauro Carvalho Chehab 
3839a0bf528SMauro Carvalho Chehab static int m88rs2000_tab_set(struct m88rs2000_state *state,
3849a0bf528SMauro Carvalho Chehab 		struct inittab *tab)
3859a0bf528SMauro Carvalho Chehab {
3869a0bf528SMauro Carvalho Chehab 	int ret = 0;
3879a0bf528SMauro Carvalho Chehab 	u8 i;
3889a0bf528SMauro Carvalho Chehab 	if (tab == NULL)
3899a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3909a0bf528SMauro Carvalho Chehab 
3919a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 255; i++) {
3929a0bf528SMauro Carvalho Chehab 		switch (tab[i].cmd) {
3939a0bf528SMauro Carvalho Chehab 		case 0x01:
394b858c331SIgor M. Liplianin 			ret = m88rs2000_writereg(state, tab[i].reg,
3959a0bf528SMauro Carvalho Chehab 				tab[i].val);
3969a0bf528SMauro Carvalho Chehab 			break;
3979a0bf528SMauro Carvalho Chehab 		case 0x10:
3989a0bf528SMauro Carvalho Chehab 			if (tab[i].reg > 0)
3999a0bf528SMauro Carvalho Chehab 				mdelay(tab[i].reg);
4009a0bf528SMauro Carvalho Chehab 			break;
4019a0bf528SMauro Carvalho Chehab 		case 0xff:
4029a0bf528SMauro Carvalho Chehab 			if (tab[i].reg == 0xaa && tab[i].val == 0xff)
4039a0bf528SMauro Carvalho Chehab 				return 0;
4049a0bf528SMauro Carvalho Chehab 		case 0x00:
4059a0bf528SMauro Carvalho Chehab 			break;
4069a0bf528SMauro Carvalho Chehab 		default:
4079a0bf528SMauro Carvalho Chehab 			return -EINVAL;
4089a0bf528SMauro Carvalho Chehab 		}
4099a0bf528SMauro Carvalho Chehab 		if (ret < 0)
4109a0bf528SMauro Carvalho Chehab 			return -ENODEV;
4119a0bf528SMauro Carvalho Chehab 	}
4129a0bf528SMauro Carvalho Chehab 	return 0;
4139a0bf528SMauro Carvalho Chehab }
4149a0bf528SMauro Carvalho Chehab 
4159a0bf528SMauro Carvalho Chehab static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
4169a0bf528SMauro Carvalho Chehab {
4179a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4189a0bf528SMauro Carvalho Chehab 	u8 data;
4199a0bf528SMauro Carvalho Chehab 
420b858c331SIgor M. Liplianin 	data = m88rs2000_readreg(state, 0xb2);
4219a0bf528SMauro Carvalho Chehab 	data |= 0x03; /* bit0 V/H, bit1 off/on */
4229a0bf528SMauro Carvalho Chehab 
4239a0bf528SMauro Carvalho Chehab 	switch (volt) {
4249a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
4259a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4269a0bf528SMauro Carvalho Chehab 		break;
4279a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
4289a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4299a0bf528SMauro Carvalho Chehab 		data |= 0x01;
4309a0bf528SMauro Carvalho Chehab 		break;
4319a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_OFF:
4329a0bf528SMauro Carvalho Chehab 		break;
4339a0bf528SMauro Carvalho Chehab 	}
4349a0bf528SMauro Carvalho Chehab 
435b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, data);
4369a0bf528SMauro Carvalho Chehab 
4379a0bf528SMauro Carvalho Chehab 	return 0;
4389a0bf528SMauro Carvalho Chehab }
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab static int m88rs2000_init(struct dvb_frontend *fe)
4419a0bf528SMauro Carvalho Chehab {
4429a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4439a0bf528SMauro Carvalho Chehab 	int ret;
4449a0bf528SMauro Carvalho Chehab 
4459a0bf528SMauro Carvalho Chehab 	deb_info("m88rs2000: init chip\n");
4469a0bf528SMauro Carvalho Chehab 	/* Setup frontend from shutdown/cold */
447081416e6SIgor M. Liplianin 	if (state->config->inittab)
448081416e6SIgor M. Liplianin 		ret = m88rs2000_tab_set(state,
449081416e6SIgor M. Liplianin 				(struct inittab *)state->config->inittab);
450081416e6SIgor M. Liplianin 	else
4519a0bf528SMauro Carvalho Chehab 		ret = m88rs2000_tab_set(state, m88rs2000_setup);
4529a0bf528SMauro Carvalho Chehab 
4539a0bf528SMauro Carvalho Chehab 	return ret;
4549a0bf528SMauro Carvalho Chehab }
4559a0bf528SMauro Carvalho Chehab 
4569a0bf528SMauro Carvalho Chehab static int m88rs2000_sleep(struct dvb_frontend *fe)
4579a0bf528SMauro Carvalho Chehab {
4589a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4599a0bf528SMauro Carvalho Chehab 	int ret;
4609a0bf528SMauro Carvalho Chehab 	/* Shutdown the frondend */
4619a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
4629a0bf528SMauro Carvalho Chehab 	return ret;
4639a0bf528SMauro Carvalho Chehab }
4649a0bf528SMauro Carvalho Chehab 
4659a0bf528SMauro Carvalho Chehab static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
4669a0bf528SMauro Carvalho Chehab {
4679a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
468b858c331SIgor M. Liplianin 	u8 reg = m88rs2000_readreg(state, 0x8c);
4699a0bf528SMauro Carvalho Chehab 
4709a0bf528SMauro Carvalho Chehab 	*status = 0;
4719a0bf528SMauro Carvalho Chehab 
4727a9d6b43SMalcolm Priestley 	if ((reg & 0xee) == 0xee) {
4739a0bf528SMauro Carvalho Chehab 		*status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
4749a0bf528SMauro Carvalho Chehab 			| FE_HAS_SYNC | FE_HAS_LOCK;
4759a0bf528SMauro Carvalho Chehab 		if (state->config->set_ts_params)
4769a0bf528SMauro Carvalho Chehab 			state->config->set_ts_params(fe, CALL_IS_READ);
4779a0bf528SMauro Carvalho Chehab 	}
4789a0bf528SMauro Carvalho Chehab 	return 0;
4799a0bf528SMauro Carvalho Chehab }
4809a0bf528SMauro Carvalho Chehab 
4819a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
4829a0bf528SMauro Carvalho Chehab {
48338f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
48438f7889cSIgor M. Liplianin 	u8 tmp0, tmp1;
48538f7889cSIgor M. Liplianin 
486b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
487b858c331SIgor M. Liplianin 	tmp0 = m88rs2000_readreg(state, 0xd8);
48838f7889cSIgor M. Liplianin 	if ((tmp0 & 0x10) != 0) {
489b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x9a, 0xb0);
49038f7889cSIgor M. Liplianin 		*ber = 0xffffffff;
49138f7889cSIgor M. Liplianin 		return 0;
49238f7889cSIgor M. Liplianin 	}
49338f7889cSIgor M. Liplianin 
494b858c331SIgor M. Liplianin 	*ber = (m88rs2000_readreg(state, 0xd7) << 8) |
495b858c331SIgor M. Liplianin 		m88rs2000_readreg(state, 0xd6);
49638f7889cSIgor M. Liplianin 
497b858c331SIgor M. Liplianin 	tmp1 = m88rs2000_readreg(state, 0xd9);
498b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4);
49938f7889cSIgor M. Liplianin 	/* needs twice */
500b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
501b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
502b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
50338f7889cSIgor M. Liplianin 
5049a0bf528SMauro Carvalho Chehab 	return 0;
5059a0bf528SMauro Carvalho Chehab }
5069a0bf528SMauro Carvalho Chehab 
5079a0bf528SMauro Carvalho Chehab static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
5089a0bf528SMauro Carvalho Chehab 	u16 *strength)
5099a0bf528SMauro Carvalho Chehab {
510a0a030bdSMalcolm Priestley 	if (fe->ops.tuner_ops.get_rf_strength)
511a0a030bdSMalcolm Priestley 		fe->ops.tuner_ops.get_rf_strength(fe, strength);
512a0a030bdSMalcolm Priestley 
5139a0bf528SMauro Carvalho Chehab 	return 0;
5149a0bf528SMauro Carvalho Chehab }
5159a0bf528SMauro Carvalho Chehab 
5169a0bf528SMauro Carvalho Chehab static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
5179a0bf528SMauro Carvalho Chehab {
51838f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
51938f7889cSIgor M. Liplianin 
520b858c331SIgor M. Liplianin 	*snr = 512 * m88rs2000_readreg(state, 0x65);
52138f7889cSIgor M. Liplianin 
5229a0bf528SMauro Carvalho Chehab 	return 0;
5239a0bf528SMauro Carvalho Chehab }
5249a0bf528SMauro Carvalho Chehab 
5259a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
5269a0bf528SMauro Carvalho Chehab {
52738f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
52838f7889cSIgor M. Liplianin 	u8 tmp;
52938f7889cSIgor M. Liplianin 
530b858c331SIgor M. Liplianin 	*ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) |
531b858c331SIgor M. Liplianin 			m88rs2000_readreg(state, 0xd4);
532b858c331SIgor M. Liplianin 	tmp = m88rs2000_readreg(state, 0xd8);
533b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp & ~0x20);
53438f7889cSIgor M. Liplianin 	/* needs two times */
535b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
536b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
53738f7889cSIgor M. Liplianin 
5389a0bf528SMauro Carvalho Chehab 	return 0;
5399a0bf528SMauro Carvalho Chehab }
5409a0bf528SMauro Carvalho Chehab 
5419a0bf528SMauro Carvalho Chehab static int m88rs2000_set_fec(struct m88rs2000_state *state,
5429a0bf528SMauro Carvalho Chehab 		fe_code_rate_t fec)
5439a0bf528SMauro Carvalho Chehab {
5449a0bf528SMauro Carvalho Chehab 	u16 fec_set;
5459a0bf528SMauro Carvalho Chehab 	switch (fec) {
5469a0bf528SMauro Carvalho Chehab 	/* This is not confirmed kept for reference */
5479a0bf528SMauro Carvalho Chehab /*	case FEC_1_2:
5489a0bf528SMauro Carvalho Chehab 		fec_set = 0x88;
5499a0bf528SMauro Carvalho Chehab 		break;
5509a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
5519a0bf528SMauro Carvalho Chehab 		fec_set = 0x68;
5529a0bf528SMauro Carvalho Chehab 		break;
5539a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
5549a0bf528SMauro Carvalho Chehab 		fec_set = 0x48;
5559a0bf528SMauro Carvalho Chehab 		break;
5569a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
5579a0bf528SMauro Carvalho Chehab 		fec_set = 0x28;
5589a0bf528SMauro Carvalho Chehab 		break;
5599a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
5609a0bf528SMauro Carvalho Chehab 		fec_set = 0x18;
5619a0bf528SMauro Carvalho Chehab 		break; */
5629a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
5639a0bf528SMauro Carvalho Chehab 	default:
5649a0bf528SMauro Carvalho Chehab 		fec_set = 0x08;
5659a0bf528SMauro Carvalho Chehab 	}
566b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x76, fec_set);
5679a0bf528SMauro Carvalho Chehab 
5689a0bf528SMauro Carvalho Chehab 	return 0;
5699a0bf528SMauro Carvalho Chehab }
5709a0bf528SMauro Carvalho Chehab 
5719a0bf528SMauro Carvalho Chehab 
5729a0bf528SMauro Carvalho Chehab static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
5739a0bf528SMauro Carvalho Chehab {
5749a0bf528SMauro Carvalho Chehab 	u8 reg;
575b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
576b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0x76);
577b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
5789a0bf528SMauro Carvalho Chehab 
5799a0bf528SMauro Carvalho Chehab 	switch (reg) {
5809a0bf528SMauro Carvalho Chehab 	case 0x88:
5819a0bf528SMauro Carvalho Chehab 		return FEC_1_2;
5829a0bf528SMauro Carvalho Chehab 	case 0x68:
5839a0bf528SMauro Carvalho Chehab 		return FEC_2_3;
5849a0bf528SMauro Carvalho Chehab 	case 0x48:
5859a0bf528SMauro Carvalho Chehab 		return FEC_3_4;
5869a0bf528SMauro Carvalho Chehab 	case 0x28:
5879a0bf528SMauro Carvalho Chehab 		return FEC_5_6;
5889a0bf528SMauro Carvalho Chehab 	case 0x18:
5899a0bf528SMauro Carvalho Chehab 		return FEC_7_8;
5909a0bf528SMauro Carvalho Chehab 	case 0x08:
5919a0bf528SMauro Carvalho Chehab 	default:
5929a0bf528SMauro Carvalho Chehab 		break;
5939a0bf528SMauro Carvalho Chehab 	}
5949a0bf528SMauro Carvalho Chehab 
5959a0bf528SMauro Carvalho Chehab 	return FEC_AUTO;
5969a0bf528SMauro Carvalho Chehab }
5979a0bf528SMauro Carvalho Chehab 
5989a0bf528SMauro Carvalho Chehab static int m88rs2000_set_frontend(struct dvb_frontend *fe)
5999a0bf528SMauro Carvalho Chehab {
6009a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
6019a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
6029a0bf528SMauro Carvalho Chehab 	fe_status_t status;
603b858c331SIgor M. Liplianin 	int i, ret = 0;
604b858c331SIgor M. Liplianin 	u32 tuner_freq;
60506af15d1SMalcolm Priestley 	s16 offset = 0;
6069a0bf528SMauro Carvalho Chehab 	u8 reg;
6079a0bf528SMauro Carvalho Chehab 
6089a0bf528SMauro Carvalho Chehab 	state->no_lock_count = 0;
6099a0bf528SMauro Carvalho Chehab 
6109a0bf528SMauro Carvalho Chehab 	if (c->delivery_system != SYS_DVBS) {
6119a0bf528SMauro Carvalho Chehab 			deb_info("%s: unsupported delivery "
6129a0bf528SMauro Carvalho Chehab 				"system selected (%d)\n",
6139a0bf528SMauro Carvalho Chehab 				__func__, c->delivery_system);
6149a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
6159a0bf528SMauro Carvalho Chehab 	}
6169a0bf528SMauro Carvalho Chehab 
6179a0bf528SMauro Carvalho Chehab 	/* Set Tuner */
618b858c331SIgor M. Liplianin 	if (fe->ops.tuner_ops.set_params)
619b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.set_params(fe);
620b858c331SIgor M. Liplianin 
6219a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6229a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6239a0bf528SMauro Carvalho Chehab 
624b858c331SIgor M. Liplianin 	if (fe->ops.tuner_ops.get_frequency)
625b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq);
626b858c331SIgor M. Liplianin 
627b858c331SIgor M. Liplianin 	if (ret < 0)
628b858c331SIgor M. Liplianin 		return -ENODEV;
629b858c331SIgor M. Liplianin 
63006af15d1SMalcolm Priestley 	offset = (s16)((s32)tuner_freq - c->frequency);
631b858c331SIgor M. Liplianin 
63206af15d1SMalcolm Priestley 	/* default mclk value 96.4285 * 2 * 1000 = 192857 */
63306af15d1SMalcolm Priestley 	if (((c->frequency % 192857) >= (192857 - 3000)) ||
63406af15d1SMalcolm Priestley 				(c->frequency % 192857) <= 3000)
63506af15d1SMalcolm Priestley 		ret = m88rs2000_writereg(state, 0x86, 0xc2);
63606af15d1SMalcolm Priestley 	else
63706af15d1SMalcolm Priestley 		ret = m88rs2000_writereg(state, 0x86, 0xc6);
638b858c331SIgor M. Liplianin 
63906af15d1SMalcolm Priestley 	ret |= m88rs2000_set_carrieroffset(fe, offset);
64006af15d1SMalcolm Priestley 	if (ret < 0)
64106af15d1SMalcolm Priestley 		return -ENODEV;
6429a0bf528SMauro Carvalho Chehab 
643dd4491dfSMalcolm Priestley 	/* Reset demod by symbol rate */
644dd4491dfSMalcolm Priestley 	if (c->symbol_rate > 27500000)
645dd4491dfSMalcolm Priestley 		ret = m88rs2000_writereg(state, 0xf1, 0xa4);
646dd4491dfSMalcolm Priestley 	else
647dd4491dfSMalcolm Priestley 		ret = m88rs2000_writereg(state, 0xf1, 0xbf);
648dd4491dfSMalcolm Priestley 
649dd4491dfSMalcolm Priestley 	ret |= m88rs2000_tab_set(state, fe_reset);
6509a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6519a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6529a0bf528SMauro Carvalho Chehab 
6539a0bf528SMauro Carvalho Chehab 	/* Unknown */
654b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0x70);
655b858c331SIgor M. Liplianin 	ret = m88rs2000_writereg(state, 0x70, reg);
6569a0bf528SMauro Carvalho Chehab 
6579a0bf528SMauro Carvalho Chehab 	/* Set FEC */
6589a0bf528SMauro Carvalho Chehab 	ret |= m88rs2000_set_fec(state, c->fec_inner);
659b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x85, 0x1);
660b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8a, 0xbf);
661b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8d, 0x1e);
662b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x90, 0xf1);
663b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x91, 0x08);
6649a0bf528SMauro Carvalho Chehab 
6659a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6669a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6679a0bf528SMauro Carvalho Chehab 
6689a0bf528SMauro Carvalho Chehab 	/* Set Symbol Rate */
6699a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
6709a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6719a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6729a0bf528SMauro Carvalho Chehab 
6739a0bf528SMauro Carvalho Chehab 	/* Set up Demod */
6749a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, fe_trigger);
6759a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6769a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6779a0bf528SMauro Carvalho Chehab 
6789a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 25; i++) {
679b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x8c);
6807a9d6b43SMalcolm Priestley 		if ((reg & 0xee) == 0xee) {
6819a0bf528SMauro Carvalho Chehab 			status = FE_HAS_LOCK;
6829a0bf528SMauro Carvalho Chehab 			break;
6839a0bf528SMauro Carvalho Chehab 		}
6849a0bf528SMauro Carvalho Chehab 		state->no_lock_count++;
6859a0bf528SMauro Carvalho Chehab 		if (state->no_lock_count == 15) {
686b858c331SIgor M. Liplianin 			reg = m88rs2000_readreg(state, 0x70);
6879a0bf528SMauro Carvalho Chehab 			reg ^= 0x4;
688b858c331SIgor M. Liplianin 			m88rs2000_writereg(state, 0x70, reg);
6899a0bf528SMauro Carvalho Chehab 			state->no_lock_count = 0;
6909a0bf528SMauro Carvalho Chehab 		}
6919a0bf528SMauro Carvalho Chehab 		msleep(20);
6929a0bf528SMauro Carvalho Chehab 	}
6939a0bf528SMauro Carvalho Chehab 
6949a0bf528SMauro Carvalho Chehab 	if (status & FE_HAS_LOCK) {
6959a0bf528SMauro Carvalho Chehab 		state->fec_inner = m88rs2000_get_fec(state);
6969a0bf528SMauro Carvalho Chehab 		/* Uknown suspect SNR level */
697b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x65);
6989a0bf528SMauro Carvalho Chehab 	}
6999a0bf528SMauro Carvalho Chehab 
7009a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = c->frequency;
7019a0bf528SMauro Carvalho Chehab 	state->symbol_rate = c->symbol_rate;
7029a0bf528SMauro Carvalho Chehab 	return 0;
7039a0bf528SMauro Carvalho Chehab }
7049a0bf528SMauro Carvalho Chehab 
7059a0bf528SMauro Carvalho Chehab static int m88rs2000_get_frontend(struct dvb_frontend *fe)
7069a0bf528SMauro Carvalho Chehab {
7079a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7089a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7099a0bf528SMauro Carvalho Chehab 	c->fec_inner = state->fec_inner;
7109a0bf528SMauro Carvalho Chehab 	c->frequency = state->tuner_frequency;
7119a0bf528SMauro Carvalho Chehab 	c->symbol_rate = state->symbol_rate;
7129a0bf528SMauro Carvalho Chehab 	return 0;
7139a0bf528SMauro Carvalho Chehab }
7149a0bf528SMauro Carvalho Chehab 
7159a0bf528SMauro Carvalho Chehab static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
7169a0bf528SMauro Carvalho Chehab {
7179a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7189a0bf528SMauro Carvalho Chehab 
7199a0bf528SMauro Carvalho Chehab 	if (enable)
720b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x84);
7219a0bf528SMauro Carvalho Chehab 	else
722b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x81);
7239a0bf528SMauro Carvalho Chehab 	udelay(10);
7249a0bf528SMauro Carvalho Chehab 	return 0;
7259a0bf528SMauro Carvalho Chehab }
7269a0bf528SMauro Carvalho Chehab 
7279a0bf528SMauro Carvalho Chehab static void m88rs2000_release(struct dvb_frontend *fe)
7289a0bf528SMauro Carvalho Chehab {
7299a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7309a0bf528SMauro Carvalho Chehab 	kfree(state);
7319a0bf528SMauro Carvalho Chehab }
7329a0bf528SMauro Carvalho Chehab 
7339a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops m88rs2000_ops = {
7349a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
7359a0bf528SMauro Carvalho Chehab 	.info = {
7369a0bf528SMauro Carvalho Chehab 		.name			= "M88RS2000 DVB-S",
7379a0bf528SMauro Carvalho Chehab 		.frequency_min		= 950000,
7389a0bf528SMauro Carvalho Chehab 		.frequency_max		= 2150000,
7399a0bf528SMauro Carvalho Chehab 		.frequency_stepsize	= 1000,	 /* kHz for QPSK frontends */
7409a0bf528SMauro Carvalho Chehab 		.frequency_tolerance	= 5000,
7419a0bf528SMauro Carvalho Chehab 		.symbol_rate_min	= 1000000,
7429a0bf528SMauro Carvalho Chehab 		.symbol_rate_max	= 45000000,
7439a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance	= 500,	/* ppm */
7449a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
7459a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
7469a0bf528SMauro Carvalho Chehab 		      FE_CAN_QPSK |
7479a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_AUTO
7489a0bf528SMauro Carvalho Chehab 	},
7499a0bf528SMauro Carvalho Chehab 
7509a0bf528SMauro Carvalho Chehab 	.release = m88rs2000_release,
7519a0bf528SMauro Carvalho Chehab 	.init = m88rs2000_init,
7529a0bf528SMauro Carvalho Chehab 	.sleep = m88rs2000_sleep,
7539a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
7549a0bf528SMauro Carvalho Chehab 	.read_status = m88rs2000_read_status,
7559a0bf528SMauro Carvalho Chehab 	.read_ber = m88rs2000_read_ber,
7569a0bf528SMauro Carvalho Chehab 	.read_signal_strength = m88rs2000_read_signal_strength,
7579a0bf528SMauro Carvalho Chehab 	.read_snr = m88rs2000_read_snr,
7589a0bf528SMauro Carvalho Chehab 	.read_ucblocks = m88rs2000_read_ucblocks,
7599a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
7609a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = m88rs2000_send_diseqc_burst,
7619a0bf528SMauro Carvalho Chehab 	.set_tone = m88rs2000_set_tone,
7629a0bf528SMauro Carvalho Chehab 	.set_voltage = m88rs2000_set_voltage,
7639a0bf528SMauro Carvalho Chehab 
7649a0bf528SMauro Carvalho Chehab 	.set_frontend = m88rs2000_set_frontend,
7659a0bf528SMauro Carvalho Chehab 	.get_frontend = m88rs2000_get_frontend,
7669a0bf528SMauro Carvalho Chehab };
7679a0bf528SMauro Carvalho Chehab 
7689a0bf528SMauro Carvalho Chehab struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
7699a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
7709a0bf528SMauro Carvalho Chehab {
7719a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = NULL;
7729a0bf528SMauro Carvalho Chehab 
7739a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
7749a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
7759a0bf528SMauro Carvalho Chehab 	if (state == NULL)
7769a0bf528SMauro Carvalho Chehab 		goto error;
7779a0bf528SMauro Carvalho Chehab 
7789a0bf528SMauro Carvalho Chehab 	/* setup the state */
7799a0bf528SMauro Carvalho Chehab 	state->config = config;
7809a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7819a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = 0;
7829a0bf528SMauro Carvalho Chehab 	state->symbol_rate = 0;
7839a0bf528SMauro Carvalho Chehab 	state->fec_inner = 0;
7849a0bf528SMauro Carvalho Chehab 
7859a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
7869a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &m88rs2000_ops,
7879a0bf528SMauro Carvalho Chehab 			sizeof(struct dvb_frontend_ops));
7889a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
7899a0bf528SMauro Carvalho Chehab 	return &state->frontend;
7909a0bf528SMauro Carvalho Chehab 
7919a0bf528SMauro Carvalho Chehab error:
7929a0bf528SMauro Carvalho Chehab 	kfree(state);
7939a0bf528SMauro Carvalho Chehab 
7949a0bf528SMauro Carvalho Chehab 	return NULL;
7959a0bf528SMauro Carvalho Chehab }
7969a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(m88rs2000_attach);
7979a0bf528SMauro Carvalho Chehab 
7989a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
7999a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
8009a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
8019a0bf528SMauro Carvalho Chehab MODULE_VERSION("1.13");
8029a0bf528SMauro Carvalho Chehab 
803