174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab 	Driver for M88RS2000 demodulator and tuner
49a0bf528SMauro Carvalho Chehab 
59a0bf528SMauro Carvalho Chehab 	Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
69a0bf528SMauro Carvalho Chehab 	Beta Driver
79a0bf528SMauro Carvalho Chehab 
89a0bf528SMauro Carvalho Chehab 	Include various calculation code from DS3000 driver.
99a0bf528SMauro Carvalho Chehab 	Copyright (C) 2009 Konstantin Dimitrov.
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab 
129a0bf528SMauro Carvalho Chehab */
139a0bf528SMauro Carvalho Chehab #include <linux/init.h>
149a0bf528SMauro Carvalho Chehab #include <linux/module.h>
159a0bf528SMauro Carvalho Chehab #include <linux/device.h>
169a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
179a0bf528SMauro Carvalho Chehab #include <linux/string.h>
189a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
199a0bf528SMauro Carvalho Chehab #include <linux/types.h>
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab 
22fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
239a0bf528SMauro Carvalho Chehab #include "m88rs2000.h"
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab struct m88rs2000_state {
269a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
279a0bf528SMauro Carvalho Chehab 	const struct m88rs2000_config *config;
289a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
299a0bf528SMauro Carvalho Chehab 	u8 no_lock_count;
309a0bf528SMauro Carvalho Chehab 	u32 tuner_frequency;
319a0bf528SMauro Carvalho Chehab 	u32 symbol_rate;
320df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec_inner;
339a0bf528SMauro Carvalho Chehab 	u8 tuner_level;
349a0bf528SMauro Carvalho Chehab 	int errmode;
359a0bf528SMauro Carvalho Chehab };
369a0bf528SMauro Carvalho Chehab 
379a0bf528SMauro Carvalho Chehab static int m88rs2000_debug;
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab module_param_named(debug, m88rs2000_debug, int, 0644);
409a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
419a0bf528SMauro Carvalho Chehab 
429a0bf528SMauro Carvalho Chehab #define dprintk(level, args...) do { \
439a0bf528SMauro Carvalho Chehab 	if (level & m88rs2000_debug) \
449a0bf528SMauro Carvalho Chehab 		printk(KERN_DEBUG "m88rs2000-fe: " args); \
459a0bf528SMauro Carvalho Chehab } while (0)
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab #define deb_info(args...)  dprintk(0x01, args)
489a0bf528SMauro Carvalho Chehab #define info(format, arg...) \
499a0bf528SMauro Carvalho Chehab 	printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
509a0bf528SMauro Carvalho Chehab 
51b858c331SIgor M. Liplianin static int m88rs2000_writereg(struct m88rs2000_state *state,
529a0bf528SMauro Carvalho Chehab 	u8 reg, u8 data)
539a0bf528SMauro Carvalho Chehab {
549a0bf528SMauro Carvalho Chehab 	int ret;
559a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
569a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
57b858c331SIgor M. Liplianin 		.addr = state->config->demod_addr,
589a0bf528SMauro Carvalho Chehab 		.flags = 0,
599a0bf528SMauro Carvalho Chehab 		.buf = buf,
609a0bf528SMauro Carvalho Chehab 		.len = 2
619a0bf528SMauro Carvalho Chehab 	};
629a0bf528SMauro Carvalho Chehab 
639a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
649a0bf528SMauro Carvalho Chehab 
659a0bf528SMauro Carvalho Chehab 	if (ret != 1)
664bd69e7bSMauro Carvalho Chehab 		deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
674bd69e7bSMauro Carvalho Chehab 			 __func__, reg, data, ret);
689a0bf528SMauro Carvalho Chehab 
699a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
709a0bf528SMauro Carvalho Chehab }
719a0bf528SMauro Carvalho Chehab 
72b858c331SIgor M. Liplianin static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg)
739a0bf528SMauro Carvalho Chehab {
749a0bf528SMauro Carvalho Chehab 	int ret;
759a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
769a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
77b858c331SIgor M. Liplianin 
789a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
799a0bf528SMauro Carvalho Chehab 		{
80b858c331SIgor M. Liplianin 			.addr = state->config->demod_addr,
819a0bf528SMauro Carvalho Chehab 			.flags = 0,
829a0bf528SMauro Carvalho Chehab 			.buf = b0,
839a0bf528SMauro Carvalho Chehab 			.len = 1
849a0bf528SMauro Carvalho Chehab 		}, {
85b858c331SIgor M. Liplianin 			.addr = state->config->demod_addr,
869a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
879a0bf528SMauro Carvalho Chehab 			.buf = b1,
889a0bf528SMauro Carvalho Chehab 			.len = 1
899a0bf528SMauro Carvalho Chehab 		}
909a0bf528SMauro Carvalho Chehab 	};
919a0bf528SMauro Carvalho Chehab 
929a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
939a0bf528SMauro Carvalho Chehab 
949a0bf528SMauro Carvalho Chehab 	if (ret != 2)
959a0bf528SMauro Carvalho Chehab 		deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
969a0bf528SMauro Carvalho Chehab 				__func__, reg, ret);
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab 	return b1[0];
999a0bf528SMauro Carvalho Chehab }
1009a0bf528SMauro Carvalho Chehab 
10106af15d1SMalcolm Priestley static u32 m88rs2000_get_mclk(struct dvb_frontend *fe)
10206af15d1SMalcolm Priestley {
10306af15d1SMalcolm Priestley 	struct m88rs2000_state *state = fe->demodulator_priv;
10406af15d1SMalcolm Priestley 	u32 mclk;
10506af15d1SMalcolm Priestley 	u8 reg;
10606af15d1SMalcolm Priestley 	/* Must not be 0x00 or 0xff */
10706af15d1SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x86);
10806af15d1SMalcolm Priestley 	if (!reg || reg == 0xff)
10906af15d1SMalcolm Priestley 		return 0;
11006af15d1SMalcolm Priestley 
11106af15d1SMalcolm Priestley 	reg /= 2;
11206af15d1SMalcolm Priestley 	reg += 1;
11306af15d1SMalcolm Priestley 
11406af15d1SMalcolm Priestley 	mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28;
11506af15d1SMalcolm Priestley 
11606af15d1SMalcolm Priestley 	return mclk;
11706af15d1SMalcolm Priestley }
11806af15d1SMalcolm Priestley 
11906af15d1SMalcolm Priestley static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset)
12006af15d1SMalcolm Priestley {
12106af15d1SMalcolm Priestley 	struct m88rs2000_state *state = fe->demodulator_priv;
12206af15d1SMalcolm Priestley 	u32 mclk;
12306af15d1SMalcolm Priestley 	s32 tmp;
12406af15d1SMalcolm Priestley 	u8 reg;
12506af15d1SMalcolm Priestley 	int ret;
12606af15d1SMalcolm Priestley 
12706af15d1SMalcolm Priestley 	mclk = m88rs2000_get_mclk(fe);
12806af15d1SMalcolm Priestley 	if (!mclk)
12906af15d1SMalcolm Priestley 		return -EINVAL;
13006af15d1SMalcolm Priestley 
13106af15d1SMalcolm Priestley 	tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk;
13206af15d1SMalcolm Priestley 	if (tmp < 0)
13306af15d1SMalcolm Priestley 		tmp += 4096;
13406af15d1SMalcolm Priestley 
13506af15d1SMalcolm Priestley 	/* Carrier Offset */
13606af15d1SMalcolm Priestley 	ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4));
13706af15d1SMalcolm Priestley 
13806af15d1SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x9d);
13906af15d1SMalcolm Priestley 	reg &= 0xf;
14006af15d1SMalcolm Priestley 	reg |= (u8)(tmp & 0xf) << 4;
14106af15d1SMalcolm Priestley 
14206af15d1SMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0x9d, reg);
14306af15d1SMalcolm Priestley 
14406af15d1SMalcolm Priestley 	return ret;
14506af15d1SMalcolm Priestley }
14606af15d1SMalcolm Priestley 
1479a0bf528SMauro Carvalho Chehab static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
1489a0bf528SMauro Carvalho Chehab {
1499a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
1509a0bf528SMauro Carvalho Chehab 	int ret;
151dd4491dfSMalcolm Priestley 	u64 temp;
152dd4491dfSMalcolm Priestley 	u32 mclk;
1539a0bf528SMauro Carvalho Chehab 	u8 b[3];
1549a0bf528SMauro Carvalho Chehab 
1559a0bf528SMauro Carvalho Chehab 	if ((srate < 1000000) || (srate > 45000000))
1569a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1579a0bf528SMauro Carvalho Chehab 
158dd4491dfSMalcolm Priestley 	mclk = m88rs2000_get_mclk(fe);
159dd4491dfSMalcolm Priestley 	if (!mclk)
160dd4491dfSMalcolm Priestley 		return -EINVAL;
161dd4491dfSMalcolm Priestley 
1629a0bf528SMauro Carvalho Chehab 	temp = srate / 1000;
163dd4491dfSMalcolm Priestley 	temp *= 1 << 24;
164dd4491dfSMalcolm Priestley 
165dd4491dfSMalcolm Priestley 	do_div(temp, mclk);
1669a0bf528SMauro Carvalho Chehab 
1679a0bf528SMauro Carvalho Chehab 	b[0] = (u8) (temp >> 16) & 0xff;
1689a0bf528SMauro Carvalho Chehab 	b[1] = (u8) (temp >> 8) & 0xff;
1699a0bf528SMauro Carvalho Chehab 	b[2] = (u8) temp & 0xff;
170dd4491dfSMalcolm Priestley 
171b858c331SIgor M. Liplianin 	ret = m88rs2000_writereg(state, 0x93, b[2]);
172b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x94, b[1]);
173b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x95, b[0]);
1749a0bf528SMauro Carvalho Chehab 
175dd4491dfSMalcolm Priestley 	if (srate > 10000000)
176dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa0, 0x20);
177dd4491dfSMalcolm Priestley 	else
178dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa0, 0x60);
179dd4491dfSMalcolm Priestley 
180dd4491dfSMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0xa1, 0xe0);
181dd4491dfSMalcolm Priestley 
182dd4491dfSMalcolm Priestley 	if (srate > 12000000)
183dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x20);
184dd4491dfSMalcolm Priestley 	else if (srate > 2800000)
185dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x98);
186dd4491dfSMalcolm Priestley 	else
187dd4491dfSMalcolm Priestley 		ret |= m88rs2000_writereg(state, 0xa3, 0x90);
188dd4491dfSMalcolm Priestley 
1899a0bf528SMauro Carvalho Chehab 	deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
1909a0bf528SMauro Carvalho Chehab 	return ret;
1919a0bf528SMauro Carvalho Chehab }
1929a0bf528SMauro Carvalho Chehab 
1939a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
1949a0bf528SMauro Carvalho Chehab 				    struct dvb_diseqc_master_cmd *m)
1959a0bf528SMauro Carvalho Chehab {
1969a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
1979a0bf528SMauro Carvalho Chehab 
1989a0bf528SMauro Carvalho Chehab 	int i;
1999a0bf528SMauro Carvalho Chehab 	u8 reg;
2009a0bf528SMauro Carvalho Chehab 	deb_info("%s\n", __func__);
201b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
202b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb2);
2039a0bf528SMauro Carvalho Chehab 	reg &= 0x3f;
204b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg);
2059a0bf528SMauro Carvalho Chehab 	for (i = 0; i <  m->msg_len; i++)
206b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0xb3 + i, m->msg[i]);
2079a0bf528SMauro Carvalho Chehab 
208b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb1);
2099a0bf528SMauro Carvalho Chehab 	reg &= 0x87;
2109a0bf528SMauro Carvalho Chehab 	reg |= ((m->msg_len - 1) << 3) | 0x07;
2119a0bf528SMauro Carvalho Chehab 	reg &= 0x7f;
212b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg);
2139a0bf528SMauro Carvalho Chehab 
2149a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 15; i++) {
215b858c331SIgor M. Liplianin 		if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0)
2169a0bf528SMauro Carvalho Chehab 			break;
2179a0bf528SMauro Carvalho Chehab 		msleep(20);
2189a0bf528SMauro Carvalho Chehab 	}
2199a0bf528SMauro Carvalho Chehab 
220b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb1);
2219a0bf528SMauro Carvalho Chehab 	if ((reg & 0x40) > 0x0) {
2229a0bf528SMauro Carvalho Chehab 		reg &= 0x7f;
2239a0bf528SMauro Carvalho Chehab 		reg |= 0x40;
224b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0xb1, reg);
2259a0bf528SMauro Carvalho Chehab 	}
2269a0bf528SMauro Carvalho Chehab 
227b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0xb2);
2289a0bf528SMauro Carvalho Chehab 	reg &= 0x3f;
2299a0bf528SMauro Carvalho Chehab 	reg |= 0x80;
230b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg);
231b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2329a0bf528SMauro Carvalho Chehab 
2339a0bf528SMauro Carvalho Chehab 
2349a0bf528SMauro Carvalho Chehab 	return 0;
2359a0bf528SMauro Carvalho Chehab }
2369a0bf528SMauro Carvalho Chehab 
2379a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
2380df289a2SMauro Carvalho Chehab 				       enum fe_sec_mini_cmd burst)
2399a0bf528SMauro Carvalho Chehab {
2409a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
2419a0bf528SMauro Carvalho Chehab 	u8 reg0, reg1;
2429a0bf528SMauro Carvalho Chehab 	deb_info("%s\n", __func__);
243b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
2449a0bf528SMauro Carvalho Chehab 	msleep(50);
245b858c331SIgor M. Liplianin 	reg0 = m88rs2000_readreg(state, 0xb1);
246b858c331SIgor M. Liplianin 	reg1 = m88rs2000_readreg(state, 0xb2);
2479a0bf528SMauro Carvalho Chehab 	/* TODO complete this section */
248b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg1);
249b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg0);
250b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2519a0bf528SMauro Carvalho Chehab 
2529a0bf528SMauro Carvalho Chehab 	return 0;
2539a0bf528SMauro Carvalho Chehab }
2549a0bf528SMauro Carvalho Chehab 
2550df289a2SMauro Carvalho Chehab static int m88rs2000_set_tone(struct dvb_frontend *fe,
2560df289a2SMauro Carvalho Chehab 			      enum fe_sec_tone_mode tone)
2579a0bf528SMauro Carvalho Chehab {
2589a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
2599a0bf528SMauro Carvalho Chehab 	u8 reg0, reg1;
260b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
261b858c331SIgor M. Liplianin 	reg0 = m88rs2000_readreg(state, 0xb1);
262b858c331SIgor M. Liplianin 	reg1 = m88rs2000_readreg(state, 0xb2);
2639a0bf528SMauro Carvalho Chehab 
2649a0bf528SMauro Carvalho Chehab 	reg1 &= 0x3f;
2659a0bf528SMauro Carvalho Chehab 
2669a0bf528SMauro Carvalho Chehab 	switch (tone) {
2679a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
2689a0bf528SMauro Carvalho Chehab 		reg0 |= 0x4;
2699a0bf528SMauro Carvalho Chehab 		reg0 &= 0xbc;
2709a0bf528SMauro Carvalho Chehab 		break;
2719a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
2729a0bf528SMauro Carvalho Chehab 		reg1 |= 0x80;
2739a0bf528SMauro Carvalho Chehab 		break;
2749a0bf528SMauro Carvalho Chehab 	default:
2759a0bf528SMauro Carvalho Chehab 		break;
2769a0bf528SMauro Carvalho Chehab 	}
277b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, reg1);
278b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb1, reg0);
279b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
2809a0bf528SMauro Carvalho Chehab 	return 0;
2819a0bf528SMauro Carvalho Chehab }
2829a0bf528SMauro Carvalho Chehab 
2839a0bf528SMauro Carvalho Chehab struct inittab {
2849a0bf528SMauro Carvalho Chehab 	u8 cmd;
2859a0bf528SMauro Carvalho Chehab 	u8 reg;
2869a0bf528SMauro Carvalho Chehab 	u8 val;
2879a0bf528SMauro Carvalho Chehab };
2889a0bf528SMauro Carvalho Chehab 
2897d3c8e8fSMalcolm Priestley static struct inittab m88rs2000_setup[] = {
2909a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
2919a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
2929a0bf528SMauro Carvalho Chehab 	{WRITE_DELAY, 0x19, 0x00},
2939a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
2949a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
2959a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0xc1},
2969a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0x81},
2979a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x86, 0xc6},
2989a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
2999a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf0, 0x22},
3009a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf1, 0xbf},
3019a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb0, 0x45},
3029a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
3039a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3049a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3059a0bf528SMauro Carvalho Chehab };
3069a0bf528SMauro Carvalho Chehab 
3077d3c8e8fSMalcolm Priestley static struct inittab m88rs2000_shutdown[] = {
3089a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0x30},
3099a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xb0, 0x00},
3109a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xf1, 0x89},
3119a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3129a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3139a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x81, 0x81},
3149a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3159a0bf528SMauro Carvalho Chehab };
3169a0bf528SMauro Carvalho Chehab 
3177d3c8e8fSMalcolm Priestley static struct inittab fe_reset[] = {
3189a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3199a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x20, 0x81},
3209a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x21, 0x80},
3219a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x10, 0x33},
3229a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x11, 0x44},
3239a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x12, 0x07},
3249a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x18, 0x20},
3259a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x28, 0x04},
3269a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x29, 0x8e},
3279a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3b, 0xff},
3289a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x32, 0x10},
3299a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x33, 0x02},
3309a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x34, 0x30},
3319a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x35, 0xff},
3329a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x38, 0x50},
3339a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x39, 0x68},
3349a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3c, 0x7f},
3359a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x3d, 0x0f},
3369a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x45, 0x20},
3379a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x46, 0x24},
3389a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x47, 0x7c},
3399a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x48, 0x16},
3409a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x49, 0x04},
3419a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4a, 0x01},
3429a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4b, 0x78},
3439a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0X4d, 0xd2},
3449a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x4e, 0x6d},
3459a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x50, 0x30},
3469a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x51, 0x30},
3479a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x54, 0x7b},
3489a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x56, 0x09},
3499a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x58, 0x59},
3509a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x59, 0x37},
3519a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x63, 0xfa},
3529a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3539a0bf528SMauro Carvalho Chehab };
3549a0bf528SMauro Carvalho Chehab 
3557d3c8e8fSMalcolm Priestley static struct inittab fe_trigger[] = {
3569a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x97, 0x04},
3579a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x99, 0x77},
3589a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9b, 0x64},
3599a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9e, 0x00},
3609a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9f, 0xf8},
3619a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x98, 0xff},
3629a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0xc0, 0x0f},
3639a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x89, 0x01},
3649a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
3659a0bf528SMauro Carvalho Chehab 	{WRITE_DELAY, 0x0a, 0x00},
3669a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x01},
3679a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x00, 0x00},
3689a0bf528SMauro Carvalho Chehab 	{DEMOD_WRITE, 0x9a, 0xb0},
3699a0bf528SMauro Carvalho Chehab 	{0xff, 0xaa, 0xff}
3709a0bf528SMauro Carvalho Chehab };
3719a0bf528SMauro Carvalho Chehab 
3729a0bf528SMauro Carvalho Chehab static int m88rs2000_tab_set(struct m88rs2000_state *state,
3739a0bf528SMauro Carvalho Chehab 		struct inittab *tab)
3749a0bf528SMauro Carvalho Chehab {
3759a0bf528SMauro Carvalho Chehab 	int ret = 0;
3769a0bf528SMauro Carvalho Chehab 	u8 i;
3779a0bf528SMauro Carvalho Chehab 	if (tab == NULL)
3789a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3799a0bf528SMauro Carvalho Chehab 
3809a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 255; i++) {
3819a0bf528SMauro Carvalho Chehab 		switch (tab[i].cmd) {
3829a0bf528SMauro Carvalho Chehab 		case 0x01:
383b858c331SIgor M. Liplianin 			ret = m88rs2000_writereg(state, tab[i].reg,
3849a0bf528SMauro Carvalho Chehab 				tab[i].val);
3859a0bf528SMauro Carvalho Chehab 			break;
3869a0bf528SMauro Carvalho Chehab 		case 0x10:
3879a0bf528SMauro Carvalho Chehab 			if (tab[i].reg > 0)
3889a0bf528SMauro Carvalho Chehab 				mdelay(tab[i].reg);
3899a0bf528SMauro Carvalho Chehab 			break;
3909a0bf528SMauro Carvalho Chehab 		case 0xff:
3919a0bf528SMauro Carvalho Chehab 			if (tab[i].reg == 0xaa && tab[i].val == 0xff)
3929a0bf528SMauro Carvalho Chehab 				return 0;
3939a0bf528SMauro Carvalho Chehab 		case 0x00:
3949a0bf528SMauro Carvalho Chehab 			break;
3959a0bf528SMauro Carvalho Chehab 		default:
3969a0bf528SMauro Carvalho Chehab 			return -EINVAL;
3979a0bf528SMauro Carvalho Chehab 		}
3989a0bf528SMauro Carvalho Chehab 		if (ret < 0)
3999a0bf528SMauro Carvalho Chehab 			return -ENODEV;
4009a0bf528SMauro Carvalho Chehab 	}
4019a0bf528SMauro Carvalho Chehab 	return 0;
4029a0bf528SMauro Carvalho Chehab }
4039a0bf528SMauro Carvalho Chehab 
4040df289a2SMauro Carvalho Chehab static int m88rs2000_set_voltage(struct dvb_frontend *fe,
4050df289a2SMauro Carvalho Chehab 				 enum fe_sec_voltage volt)
4069a0bf528SMauro Carvalho Chehab {
4079a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4089a0bf528SMauro Carvalho Chehab 	u8 data;
4099a0bf528SMauro Carvalho Chehab 
410b858c331SIgor M. Liplianin 	data = m88rs2000_readreg(state, 0xb2);
4119a0bf528SMauro Carvalho Chehab 	data |= 0x03; /* bit0 V/H, bit1 off/on */
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 	switch (volt) {
4149a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
4159a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4169a0bf528SMauro Carvalho Chehab 		break;
4179a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
4189a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4199a0bf528SMauro Carvalho Chehab 		data |= 0x01;
4209a0bf528SMauro Carvalho Chehab 		break;
4219a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_OFF:
4229a0bf528SMauro Carvalho Chehab 		break;
4239a0bf528SMauro Carvalho Chehab 	}
4249a0bf528SMauro Carvalho Chehab 
425b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, data);
4269a0bf528SMauro Carvalho Chehab 
4279a0bf528SMauro Carvalho Chehab 	return 0;
4289a0bf528SMauro Carvalho Chehab }
4299a0bf528SMauro Carvalho Chehab 
4309a0bf528SMauro Carvalho Chehab static int m88rs2000_init(struct dvb_frontend *fe)
4319a0bf528SMauro Carvalho Chehab {
4329a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4339a0bf528SMauro Carvalho Chehab 	int ret;
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 	deb_info("m88rs2000: init chip\n");
4369a0bf528SMauro Carvalho Chehab 	/* Setup frontend from shutdown/cold */
437081416e6SIgor M. Liplianin 	if (state->config->inittab)
438081416e6SIgor M. Liplianin 		ret = m88rs2000_tab_set(state,
439081416e6SIgor M. Liplianin 				(struct inittab *)state->config->inittab);
440081416e6SIgor M. Liplianin 	else
4419a0bf528SMauro Carvalho Chehab 		ret = m88rs2000_tab_set(state, m88rs2000_setup);
4429a0bf528SMauro Carvalho Chehab 
4439a0bf528SMauro Carvalho Chehab 	return ret;
4449a0bf528SMauro Carvalho Chehab }
4459a0bf528SMauro Carvalho Chehab 
4469a0bf528SMauro Carvalho Chehab static int m88rs2000_sleep(struct dvb_frontend *fe)
4479a0bf528SMauro Carvalho Chehab {
4489a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4499a0bf528SMauro Carvalho Chehab 	int ret;
4509a0bf528SMauro Carvalho Chehab 	/* Shutdown the frondend */
4519a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
4529a0bf528SMauro Carvalho Chehab 	return ret;
4539a0bf528SMauro Carvalho Chehab }
4549a0bf528SMauro Carvalho Chehab 
4550df289a2SMauro Carvalho Chehab static int m88rs2000_read_status(struct dvb_frontend *fe,
4560df289a2SMauro Carvalho Chehab 				 enum fe_status *status)
4579a0bf528SMauro Carvalho Chehab {
4589a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
459b858c331SIgor M. Liplianin 	u8 reg = m88rs2000_readreg(state, 0x8c);
4609a0bf528SMauro Carvalho Chehab 
4619a0bf528SMauro Carvalho Chehab 	*status = 0;
4629a0bf528SMauro Carvalho Chehab 
4637a9d6b43SMalcolm Priestley 	if ((reg & 0xee) == 0xee) {
4649a0bf528SMauro Carvalho Chehab 		*status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
4659a0bf528SMauro Carvalho Chehab 			| FE_HAS_SYNC | FE_HAS_LOCK;
4669a0bf528SMauro Carvalho Chehab 		if (state->config->set_ts_params)
4679a0bf528SMauro Carvalho Chehab 			state->config->set_ts_params(fe, CALL_IS_READ);
4689a0bf528SMauro Carvalho Chehab 	}
4699a0bf528SMauro Carvalho Chehab 	return 0;
4709a0bf528SMauro Carvalho Chehab }
4719a0bf528SMauro Carvalho Chehab 
4729a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
4739a0bf528SMauro Carvalho Chehab {
47438f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
47538f7889cSIgor M. Liplianin 	u8 tmp0, tmp1;
47638f7889cSIgor M. Liplianin 
477b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
478b858c331SIgor M. Liplianin 	tmp0 = m88rs2000_readreg(state, 0xd8);
47938f7889cSIgor M. Liplianin 	if ((tmp0 & 0x10) != 0) {
480b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x9a, 0xb0);
48138f7889cSIgor M. Liplianin 		*ber = 0xffffffff;
48238f7889cSIgor M. Liplianin 		return 0;
48338f7889cSIgor M. Liplianin 	}
48438f7889cSIgor M. Liplianin 
485b858c331SIgor M. Liplianin 	*ber = (m88rs2000_readreg(state, 0xd7) << 8) |
486b858c331SIgor M. Liplianin 		m88rs2000_readreg(state, 0xd6);
48738f7889cSIgor M. Liplianin 
488b858c331SIgor M. Liplianin 	tmp1 = m88rs2000_readreg(state, 0xd9);
489b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4);
49038f7889cSIgor M. Liplianin 	/* needs twice */
491b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
492b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
493b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
49438f7889cSIgor M. Liplianin 
4959a0bf528SMauro Carvalho Chehab 	return 0;
4969a0bf528SMauro Carvalho Chehab }
4979a0bf528SMauro Carvalho Chehab 
4989a0bf528SMauro Carvalho Chehab static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
4999a0bf528SMauro Carvalho Chehab 	u16 *strength)
5009a0bf528SMauro Carvalho Chehab {
501a0a030bdSMalcolm Priestley 	if (fe->ops.tuner_ops.get_rf_strength)
502a0a030bdSMalcolm Priestley 		fe->ops.tuner_ops.get_rf_strength(fe, strength);
503a0a030bdSMalcolm Priestley 
5049a0bf528SMauro Carvalho Chehab 	return 0;
5059a0bf528SMauro Carvalho Chehab }
5069a0bf528SMauro Carvalho Chehab 
5079a0bf528SMauro Carvalho Chehab static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
5089a0bf528SMauro Carvalho Chehab {
50938f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
51038f7889cSIgor M. Liplianin 
511b858c331SIgor M. Liplianin 	*snr = 512 * m88rs2000_readreg(state, 0x65);
51238f7889cSIgor M. Liplianin 
5139a0bf528SMauro Carvalho Chehab 	return 0;
5149a0bf528SMauro Carvalho Chehab }
5159a0bf528SMauro Carvalho Chehab 
5169a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
5179a0bf528SMauro Carvalho Chehab {
51838f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
51938f7889cSIgor M. Liplianin 	u8 tmp;
52038f7889cSIgor M. Liplianin 
521b858c331SIgor M. Liplianin 	*ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) |
522b858c331SIgor M. Liplianin 			m88rs2000_readreg(state, 0xd4);
523b858c331SIgor M. Liplianin 	tmp = m88rs2000_readreg(state, 0xd8);
524b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp & ~0x20);
52538f7889cSIgor M. Liplianin 	/* needs two times */
526b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
527b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
52838f7889cSIgor M. Liplianin 
5299a0bf528SMauro Carvalho Chehab 	return 0;
5309a0bf528SMauro Carvalho Chehab }
5319a0bf528SMauro Carvalho Chehab 
5329a0bf528SMauro Carvalho Chehab static int m88rs2000_set_fec(struct m88rs2000_state *state,
5330df289a2SMauro Carvalho Chehab 			     enum fe_code_rate fec)
5349a0bf528SMauro Carvalho Chehab {
53549c44802SMalcolm Priestley 	u8 fec_set, reg;
53649c44802SMalcolm Priestley 	int ret;
53749c44802SMalcolm Priestley 
5389a0bf528SMauro Carvalho Chehab 	switch (fec) {
53949c44802SMalcolm Priestley 	case FEC_1_2:
54049c44802SMalcolm Priestley 		fec_set = 0x8;
5419a0bf528SMauro Carvalho Chehab 		break;
5429a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
54349c44802SMalcolm Priestley 		fec_set = 0x10;
5449a0bf528SMauro Carvalho Chehab 		break;
5459a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
54649c44802SMalcolm Priestley 		fec_set = 0x20;
5479a0bf528SMauro Carvalho Chehab 		break;
5489a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
54949c44802SMalcolm Priestley 		fec_set = 0x40;
5509a0bf528SMauro Carvalho Chehab 		break;
5519a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
55249c44802SMalcolm Priestley 		fec_set = 0x80;
55349c44802SMalcolm Priestley 		break;
5549a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
5559a0bf528SMauro Carvalho Chehab 	default:
55649c44802SMalcolm Priestley 		fec_set = 0x0;
5579a0bf528SMauro Carvalho Chehab 	}
5589a0bf528SMauro Carvalho Chehab 
55949c44802SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x70);
56049c44802SMalcolm Priestley 	reg &= 0x7;
56149c44802SMalcolm Priestley 	ret = m88rs2000_writereg(state, 0x70, reg | fec_set);
56249c44802SMalcolm Priestley 
56349c44802SMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0x76, 0x8);
56449c44802SMalcolm Priestley 
56549c44802SMalcolm Priestley 	return ret;
56649c44802SMalcolm Priestley }
5679a0bf528SMauro Carvalho Chehab 
5680df289a2SMauro Carvalho Chehab static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state)
5699a0bf528SMauro Carvalho Chehab {
5709a0bf528SMauro Carvalho Chehab 	u8 reg;
571b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
572b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0x76);
573b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
5749a0bf528SMauro Carvalho Chehab 
575a6d8e68bSMalcolm Priestley 	reg &= 0xf0;
576a6d8e68bSMalcolm Priestley 	reg >>= 5;
577a6d8e68bSMalcolm Priestley 
5789a0bf528SMauro Carvalho Chehab 	switch (reg) {
579a6d8e68bSMalcolm Priestley 	case 0x4:
5809a0bf528SMauro Carvalho Chehab 		return FEC_1_2;
581a6d8e68bSMalcolm Priestley 	case 0x3:
5829a0bf528SMauro Carvalho Chehab 		return FEC_2_3;
583a6d8e68bSMalcolm Priestley 	case 0x2:
5849a0bf528SMauro Carvalho Chehab 		return FEC_3_4;
585a6d8e68bSMalcolm Priestley 	case 0x1:
5869a0bf528SMauro Carvalho Chehab 		return FEC_5_6;
587a6d8e68bSMalcolm Priestley 	case 0x0:
5889a0bf528SMauro Carvalho Chehab 		return FEC_7_8;
5899a0bf528SMauro Carvalho Chehab 	default:
5909a0bf528SMauro Carvalho Chehab 		break;
5919a0bf528SMauro Carvalho Chehab 	}
5929a0bf528SMauro Carvalho Chehab 
5939a0bf528SMauro Carvalho Chehab 	return FEC_AUTO;
5949a0bf528SMauro Carvalho Chehab }
5959a0bf528SMauro Carvalho Chehab 
5969a0bf528SMauro Carvalho Chehab static int m88rs2000_set_frontend(struct dvb_frontend *fe)
5979a0bf528SMauro Carvalho Chehab {
5989a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
5999a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
600cac1c639SColin Ian King 	enum fe_status status = 0;
601b858c331SIgor M. Liplianin 	int i, ret = 0;
602b858c331SIgor M. Liplianin 	u32 tuner_freq;
60306af15d1SMalcolm Priestley 	s16 offset = 0;
6049a0bf528SMauro Carvalho Chehab 	u8 reg;
6059a0bf528SMauro Carvalho Chehab 
6069a0bf528SMauro Carvalho Chehab 	state->no_lock_count = 0;
6079a0bf528SMauro Carvalho Chehab 
6089a0bf528SMauro Carvalho Chehab 	if (c->delivery_system != SYS_DVBS) {
6094bd69e7bSMauro Carvalho Chehab 		deb_info("%s: unsupported delivery system selected (%d)\n",
6109a0bf528SMauro Carvalho Chehab 			 __func__, c->delivery_system);
6119a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
6129a0bf528SMauro Carvalho Chehab 	}
6139a0bf528SMauro Carvalho Chehab 
6149a0bf528SMauro Carvalho Chehab 	/* Set Tuner */
615b858c331SIgor M. Liplianin 	if (fe->ops.tuner_ops.set_params)
616b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.set_params(fe);
617b858c331SIgor M. Liplianin 
6189a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6199a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6209a0bf528SMauro Carvalho Chehab 
62174a6799cSMauro Carvalho Chehab 	if (fe->ops.tuner_ops.get_frequency) {
622b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq);
623b858c331SIgor M. Liplianin 
624b858c331SIgor M. Liplianin 		if (ret < 0)
625b858c331SIgor M. Liplianin 			return -ENODEV;
626b858c331SIgor M. Liplianin 
62706af15d1SMalcolm Priestley 		offset = (s16)((s32)tuner_freq - c->frequency);
62874a6799cSMauro Carvalho Chehab 	} else {
62974a6799cSMauro Carvalho Chehab 		offset = 0;
63074a6799cSMauro Carvalho Chehab 	}
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 	/* Set FEC */
65449c44802SMalcolm Priestley 	ret = m88rs2000_set_fec(state, c->fec_inner);
655b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x85, 0x1);
656b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8a, 0xbf);
657b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8d, 0x1e);
658b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x90, 0xf1);
659b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x91, 0x08);
6609a0bf528SMauro Carvalho Chehab 
6619a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6629a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6639a0bf528SMauro Carvalho Chehab 
6649a0bf528SMauro Carvalho Chehab 	/* Set Symbol Rate */
6659a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
6669a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6679a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6689a0bf528SMauro Carvalho Chehab 
6699a0bf528SMauro Carvalho Chehab 	/* Set up Demod */
6709a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, fe_trigger);
6719a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6729a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6739a0bf528SMauro Carvalho Chehab 
6749a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 25; i++) {
675b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x8c);
6767a9d6b43SMalcolm Priestley 		if ((reg & 0xee) == 0xee) {
6779a0bf528SMauro Carvalho Chehab 			status = FE_HAS_LOCK;
6789a0bf528SMauro Carvalho Chehab 			break;
6799a0bf528SMauro Carvalho Chehab 		}
6809a0bf528SMauro Carvalho Chehab 		state->no_lock_count++;
6819a0bf528SMauro Carvalho Chehab 		if (state->no_lock_count == 15) {
682b858c331SIgor M. Liplianin 			reg = m88rs2000_readreg(state, 0x70);
6839a0bf528SMauro Carvalho Chehab 			reg ^= 0x4;
684b858c331SIgor M. Liplianin 			m88rs2000_writereg(state, 0x70, reg);
6859a0bf528SMauro Carvalho Chehab 			state->no_lock_count = 0;
6869a0bf528SMauro Carvalho Chehab 		}
6879a0bf528SMauro Carvalho Chehab 		msleep(20);
6889a0bf528SMauro Carvalho Chehab 	}
6899a0bf528SMauro Carvalho Chehab 
6909a0bf528SMauro Carvalho Chehab 	if (status & FE_HAS_LOCK) {
6919a0bf528SMauro Carvalho Chehab 		state->fec_inner = m88rs2000_get_fec(state);
692868c9a17SMauro Carvalho Chehab 		/* Unknown suspect SNR level */
693b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x65);
6949a0bf528SMauro Carvalho Chehab 	}
6959a0bf528SMauro Carvalho Chehab 
6969a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = c->frequency;
6979a0bf528SMauro Carvalho Chehab 	state->symbol_rate = c->symbol_rate;
6989a0bf528SMauro Carvalho Chehab 	return 0;
6999a0bf528SMauro Carvalho Chehab }
7009a0bf528SMauro Carvalho Chehab 
7017e3e68bcSMauro Carvalho Chehab static int m88rs2000_get_frontend(struct dvb_frontend *fe,
7027e3e68bcSMauro Carvalho Chehab 				  struct dtv_frontend_properties *c)
7039a0bf528SMauro Carvalho Chehab {
7049a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7057e3e68bcSMauro Carvalho Chehab 
7069a0bf528SMauro Carvalho Chehab 	c->fec_inner = state->fec_inner;
7079a0bf528SMauro Carvalho Chehab 	c->frequency = state->tuner_frequency;
7089a0bf528SMauro Carvalho Chehab 	c->symbol_rate = state->symbol_rate;
7099a0bf528SMauro Carvalho Chehab 	return 0;
7109a0bf528SMauro Carvalho Chehab }
7119a0bf528SMauro Carvalho Chehab 
7128272d0a0SMalcolm Priestley static int m88rs2000_get_tune_settings(struct dvb_frontend *fe,
7138272d0a0SMalcolm Priestley 	struct dvb_frontend_tune_settings *tune)
7148272d0a0SMalcolm Priestley {
7158272d0a0SMalcolm Priestley 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7168272d0a0SMalcolm Priestley 
7178272d0a0SMalcolm Priestley 	if (c->symbol_rate > 3000000)
7188272d0a0SMalcolm Priestley 		tune->min_delay_ms = 2000;
7198272d0a0SMalcolm Priestley 	else
7208272d0a0SMalcolm Priestley 		tune->min_delay_ms = 3000;
7218272d0a0SMalcolm Priestley 
7228272d0a0SMalcolm Priestley 	tune->step_size = c->symbol_rate / 16000;
7238272d0a0SMalcolm Priestley 	tune->max_drift = c->symbol_rate / 2000;
7248272d0a0SMalcolm Priestley 
7258272d0a0SMalcolm Priestley 	return 0;
7268272d0a0SMalcolm Priestley }
7278272d0a0SMalcolm Priestley 
7289a0bf528SMauro Carvalho Chehab static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
7299a0bf528SMauro Carvalho Chehab {
7309a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7319a0bf528SMauro Carvalho Chehab 
7329a0bf528SMauro Carvalho Chehab 	if (enable)
733b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x84);
7349a0bf528SMauro Carvalho Chehab 	else
735b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x81);
7369a0bf528SMauro Carvalho Chehab 	udelay(10);
7379a0bf528SMauro Carvalho Chehab 	return 0;
7389a0bf528SMauro Carvalho Chehab }
7399a0bf528SMauro Carvalho Chehab 
7409a0bf528SMauro Carvalho Chehab static void m88rs2000_release(struct dvb_frontend *fe)
7419a0bf528SMauro Carvalho Chehab {
7429a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7439a0bf528SMauro Carvalho Chehab 	kfree(state);
7449a0bf528SMauro Carvalho Chehab }
7459a0bf528SMauro Carvalho Chehab 
746bd336e63SMax Kellermann static const struct dvb_frontend_ops m88rs2000_ops = {
7479a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
7489a0bf528SMauro Carvalho Chehab 	.info = {
7499a0bf528SMauro Carvalho Chehab 		.name			= "M88RS2000 DVB-S",
750f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  950 * MHz,
751f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 2150 * MHz,
752f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	= 1 * MHz,
753f1b1eabfSMauro Carvalho Chehab 		.frequency_tolerance_hz	= 5 * MHz,
7549a0bf528SMauro Carvalho Chehab 		.symbol_rate_min	= 1000000,
7559a0bf528SMauro Carvalho Chehab 		.symbol_rate_max	= 45000000,
7569a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance	= 500,	/* ppm */
7579a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
7589a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
7593c8023a7SMalcolm Priestley 		      FE_CAN_QPSK | FE_CAN_INVERSION_AUTO |
7609a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_AUTO
7619a0bf528SMauro Carvalho Chehab 	},
7629a0bf528SMauro Carvalho Chehab 
7639a0bf528SMauro Carvalho Chehab 	.release = m88rs2000_release,
7649a0bf528SMauro Carvalho Chehab 	.init = m88rs2000_init,
7659a0bf528SMauro Carvalho Chehab 	.sleep = m88rs2000_sleep,
7669a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
7679a0bf528SMauro Carvalho Chehab 	.read_status = m88rs2000_read_status,
7689a0bf528SMauro Carvalho Chehab 	.read_ber = m88rs2000_read_ber,
7699a0bf528SMauro Carvalho Chehab 	.read_signal_strength = m88rs2000_read_signal_strength,
7709a0bf528SMauro Carvalho Chehab 	.read_snr = m88rs2000_read_snr,
7719a0bf528SMauro Carvalho Chehab 	.read_ucblocks = m88rs2000_read_ucblocks,
7729a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
7739a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = m88rs2000_send_diseqc_burst,
7749a0bf528SMauro Carvalho Chehab 	.set_tone = m88rs2000_set_tone,
7759a0bf528SMauro Carvalho Chehab 	.set_voltage = m88rs2000_set_voltage,
7769a0bf528SMauro Carvalho Chehab 
7779a0bf528SMauro Carvalho Chehab 	.set_frontend = m88rs2000_set_frontend,
7789a0bf528SMauro Carvalho Chehab 	.get_frontend = m88rs2000_get_frontend,
7798272d0a0SMalcolm Priestley 	.get_tune_settings = m88rs2000_get_tune_settings,
7809a0bf528SMauro Carvalho Chehab };
7819a0bf528SMauro Carvalho Chehab 
7829a0bf528SMauro Carvalho Chehab struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
7839a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
7849a0bf528SMauro Carvalho Chehab {
7859a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = NULL;
7869a0bf528SMauro Carvalho Chehab 
7879a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
7889a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
7899a0bf528SMauro Carvalho Chehab 	if (state == NULL)
7909a0bf528SMauro Carvalho Chehab 		goto error;
7919a0bf528SMauro Carvalho Chehab 
7929a0bf528SMauro Carvalho Chehab 	/* setup the state */
7939a0bf528SMauro Carvalho Chehab 	state->config = config;
7949a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7959a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = 0;
7969a0bf528SMauro Carvalho Chehab 	state->symbol_rate = 0;
7979a0bf528SMauro Carvalho Chehab 	state->fec_inner = 0;
7989a0bf528SMauro Carvalho Chehab 
7999a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
8009a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &m88rs2000_ops,
8019a0bf528SMauro Carvalho Chehab 			sizeof(struct dvb_frontend_ops));
8029a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
8039a0bf528SMauro Carvalho Chehab 	return &state->frontend;
8049a0bf528SMauro Carvalho Chehab 
8059a0bf528SMauro Carvalho Chehab error:
8069a0bf528SMauro Carvalho Chehab 	kfree(state);
8079a0bf528SMauro Carvalho Chehab 
8089a0bf528SMauro Carvalho Chehab 	return NULL;
8099a0bf528SMauro Carvalho Chehab }
8109a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(m88rs2000_attach);
8119a0bf528SMauro Carvalho Chehab 
8129a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
8139a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
8149a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
8159a0bf528SMauro Carvalho Chehab MODULE_VERSION("1.13");
8169a0bf528SMauro Carvalho Chehab 
817