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 
m88rs2000_writereg(struct m88rs2000_state * state,u8 reg,u8 data)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 
m88rs2000_readreg(struct m88rs2000_state * state,u8 reg)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 
m88rs2000_get_mclk(struct dvb_frontend * fe)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 
m88rs2000_set_carrieroffset(struct dvb_frontend * fe,s16 offset)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 
m88rs2000_set_symbolrate(struct dvb_frontend * fe,u32 srate)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 
m88rs2000_send_diseqc_msg(struct dvb_frontend * fe,struct dvb_diseqc_master_cmd * m)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 
m88rs2000_send_diseqc_burst(struct dvb_frontend * fe,enum fe_sec_mini_cmd burst)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 
m88rs2000_set_tone(struct dvb_frontend * fe,enum fe_sec_tone_mode tone)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 
m88rs2000_tab_set(struct m88rs2000_state * state,struct inittab * tab)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;
393af7ab662SGustavo A. R. Silva 			break;
3949a0bf528SMauro Carvalho Chehab 		case 0x00:
3959a0bf528SMauro Carvalho Chehab 			break;
3969a0bf528SMauro Carvalho Chehab 		default:
3979a0bf528SMauro Carvalho Chehab 			return -EINVAL;
3989a0bf528SMauro Carvalho Chehab 		}
3999a0bf528SMauro Carvalho Chehab 		if (ret < 0)
4009a0bf528SMauro Carvalho Chehab 			return -ENODEV;
4019a0bf528SMauro Carvalho Chehab 	}
4029a0bf528SMauro Carvalho Chehab 	return 0;
4039a0bf528SMauro Carvalho Chehab }
4049a0bf528SMauro Carvalho Chehab 
m88rs2000_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage volt)4050df289a2SMauro Carvalho Chehab static int m88rs2000_set_voltage(struct dvb_frontend *fe,
4060df289a2SMauro Carvalho Chehab 				 enum fe_sec_voltage volt)
4079a0bf528SMauro Carvalho Chehab {
4089a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4099a0bf528SMauro Carvalho Chehab 	u8 data;
4109a0bf528SMauro Carvalho Chehab 
411b858c331SIgor M. Liplianin 	data = m88rs2000_readreg(state, 0xb2);
4129a0bf528SMauro Carvalho Chehab 	data |= 0x03; /* bit0 V/H, bit1 off/on */
4139a0bf528SMauro Carvalho Chehab 
4149a0bf528SMauro Carvalho Chehab 	switch (volt) {
4159a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
4169a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4179a0bf528SMauro Carvalho Chehab 		break;
4189a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
4199a0bf528SMauro Carvalho Chehab 		data &= ~0x03;
4209a0bf528SMauro Carvalho Chehab 		data |= 0x01;
4219a0bf528SMauro Carvalho Chehab 		break;
4229a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_OFF:
4239a0bf528SMauro Carvalho Chehab 		break;
4249a0bf528SMauro Carvalho Chehab 	}
4259a0bf528SMauro Carvalho Chehab 
426b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xb2, data);
4279a0bf528SMauro Carvalho Chehab 
4289a0bf528SMauro Carvalho Chehab 	return 0;
4299a0bf528SMauro Carvalho Chehab }
4309a0bf528SMauro Carvalho Chehab 
m88rs2000_init(struct dvb_frontend * fe)4319a0bf528SMauro Carvalho Chehab static int m88rs2000_init(struct dvb_frontend *fe)
4329a0bf528SMauro Carvalho Chehab {
4339a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4349a0bf528SMauro Carvalho Chehab 	int ret;
4359a0bf528SMauro Carvalho Chehab 
4369a0bf528SMauro Carvalho Chehab 	deb_info("m88rs2000: init chip\n");
4379a0bf528SMauro Carvalho Chehab 	/* Setup frontend from shutdown/cold */
438081416e6SIgor M. Liplianin 	if (state->config->inittab)
439081416e6SIgor M. Liplianin 		ret = m88rs2000_tab_set(state,
440081416e6SIgor M. Liplianin 				(struct inittab *)state->config->inittab);
441081416e6SIgor M. Liplianin 	else
4429a0bf528SMauro Carvalho Chehab 		ret = m88rs2000_tab_set(state, m88rs2000_setup);
4439a0bf528SMauro Carvalho Chehab 
4449a0bf528SMauro Carvalho Chehab 	return ret;
4459a0bf528SMauro Carvalho Chehab }
4469a0bf528SMauro Carvalho Chehab 
m88rs2000_sleep(struct dvb_frontend * fe)4479a0bf528SMauro Carvalho Chehab static int m88rs2000_sleep(struct dvb_frontend *fe)
4489a0bf528SMauro Carvalho Chehab {
4499a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
4509a0bf528SMauro Carvalho Chehab 	int ret;
4519a0bf528SMauro Carvalho Chehab 	/* Shutdown the frondend */
4529a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
4539a0bf528SMauro Carvalho Chehab 	return ret;
4549a0bf528SMauro Carvalho Chehab }
4559a0bf528SMauro Carvalho Chehab 
m88rs2000_read_status(struct dvb_frontend * fe,enum fe_status * status)4560df289a2SMauro Carvalho Chehab static int m88rs2000_read_status(struct dvb_frontend *fe,
4570df289a2SMauro Carvalho Chehab 				 enum fe_status *status)
4589a0bf528SMauro Carvalho Chehab {
4599a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
460b858c331SIgor M. Liplianin 	u8 reg = m88rs2000_readreg(state, 0x8c);
4619a0bf528SMauro Carvalho Chehab 
4629a0bf528SMauro Carvalho Chehab 	*status = 0;
4639a0bf528SMauro Carvalho Chehab 
4647a9d6b43SMalcolm Priestley 	if ((reg & 0xee) == 0xee) {
4659a0bf528SMauro Carvalho Chehab 		*status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
4669a0bf528SMauro Carvalho Chehab 			| FE_HAS_SYNC | FE_HAS_LOCK;
4679a0bf528SMauro Carvalho Chehab 		if (state->config->set_ts_params)
4689a0bf528SMauro Carvalho Chehab 			state->config->set_ts_params(fe, CALL_IS_READ);
4699a0bf528SMauro Carvalho Chehab 	}
4709a0bf528SMauro Carvalho Chehab 	return 0;
4719a0bf528SMauro Carvalho Chehab }
4729a0bf528SMauro Carvalho Chehab 
m88rs2000_read_ber(struct dvb_frontend * fe,u32 * ber)4739a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
4749a0bf528SMauro Carvalho Chehab {
47538f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
47638f7889cSIgor M. Liplianin 	u8 tmp0, tmp1;
47738f7889cSIgor M. Liplianin 
478b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
479b858c331SIgor M. Liplianin 	tmp0 = m88rs2000_readreg(state, 0xd8);
48038f7889cSIgor M. Liplianin 	if ((tmp0 & 0x10) != 0) {
481b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x9a, 0xb0);
48238f7889cSIgor M. Liplianin 		*ber = 0xffffffff;
48338f7889cSIgor M. Liplianin 		return 0;
48438f7889cSIgor M. Liplianin 	}
48538f7889cSIgor M. Liplianin 
486b858c331SIgor M. Liplianin 	*ber = (m88rs2000_readreg(state, 0xd7) << 8) |
487b858c331SIgor M. Liplianin 		m88rs2000_readreg(state, 0xd6);
48838f7889cSIgor M. Liplianin 
489b858c331SIgor M. Liplianin 	tmp1 = m88rs2000_readreg(state, 0xd9);
490b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4);
49138f7889cSIgor M. Liplianin 	/* needs twice */
492b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
493b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
494b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
49538f7889cSIgor M. Liplianin 
4969a0bf528SMauro Carvalho Chehab 	return 0;
4979a0bf528SMauro Carvalho Chehab }
4989a0bf528SMauro Carvalho Chehab 
m88rs2000_read_signal_strength(struct dvb_frontend * fe,u16 * strength)4999a0bf528SMauro Carvalho Chehab static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
5009a0bf528SMauro Carvalho Chehab 	u16 *strength)
5019a0bf528SMauro Carvalho Chehab {
502a0a030bdSMalcolm Priestley 	if (fe->ops.tuner_ops.get_rf_strength)
503a0a030bdSMalcolm Priestley 		fe->ops.tuner_ops.get_rf_strength(fe, strength);
504a0a030bdSMalcolm Priestley 
5059a0bf528SMauro Carvalho Chehab 	return 0;
5069a0bf528SMauro Carvalho Chehab }
5079a0bf528SMauro Carvalho Chehab 
m88rs2000_read_snr(struct dvb_frontend * fe,u16 * snr)5089a0bf528SMauro Carvalho Chehab static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
5099a0bf528SMauro Carvalho Chehab {
51038f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
51138f7889cSIgor M. Liplianin 
512b858c331SIgor M. Liplianin 	*snr = 512 * m88rs2000_readreg(state, 0x65);
51338f7889cSIgor M. Liplianin 
5149a0bf528SMauro Carvalho Chehab 	return 0;
5159a0bf528SMauro Carvalho Chehab }
5169a0bf528SMauro Carvalho Chehab 
m88rs2000_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)5179a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
5189a0bf528SMauro Carvalho Chehab {
51938f7889cSIgor M. Liplianin 	struct m88rs2000_state *state = fe->demodulator_priv;
52038f7889cSIgor M. Liplianin 	u8 tmp;
52138f7889cSIgor M. Liplianin 
522b858c331SIgor M. Liplianin 	*ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) |
523b858c331SIgor M. Liplianin 			m88rs2000_readreg(state, 0xd4);
524b858c331SIgor M. Liplianin 	tmp = m88rs2000_readreg(state, 0xd8);
525b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp & ~0x20);
52638f7889cSIgor M. Liplianin 	/* needs two times */
527b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
528b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0xd8, tmp | 0x20);
52938f7889cSIgor M. Liplianin 
5309a0bf528SMauro Carvalho Chehab 	return 0;
5319a0bf528SMauro Carvalho Chehab }
5329a0bf528SMauro Carvalho Chehab 
m88rs2000_set_fec(struct m88rs2000_state * state,enum fe_code_rate fec)5339a0bf528SMauro Carvalho Chehab static int m88rs2000_set_fec(struct m88rs2000_state *state,
5340df289a2SMauro Carvalho Chehab 			     enum fe_code_rate fec)
5359a0bf528SMauro Carvalho Chehab {
53649c44802SMalcolm Priestley 	u8 fec_set, reg;
53749c44802SMalcolm Priestley 	int ret;
53849c44802SMalcolm Priestley 
5399a0bf528SMauro Carvalho Chehab 	switch (fec) {
54049c44802SMalcolm Priestley 	case FEC_1_2:
54149c44802SMalcolm Priestley 		fec_set = 0x8;
5429a0bf528SMauro Carvalho Chehab 		break;
5439a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
54449c44802SMalcolm Priestley 		fec_set = 0x10;
5459a0bf528SMauro Carvalho Chehab 		break;
5469a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
54749c44802SMalcolm Priestley 		fec_set = 0x20;
5489a0bf528SMauro Carvalho Chehab 		break;
5499a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
55049c44802SMalcolm Priestley 		fec_set = 0x40;
5519a0bf528SMauro Carvalho Chehab 		break;
5529a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
55349c44802SMalcolm Priestley 		fec_set = 0x80;
55449c44802SMalcolm Priestley 		break;
5559a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
5569a0bf528SMauro Carvalho Chehab 	default:
55749c44802SMalcolm Priestley 		fec_set = 0x0;
5589a0bf528SMauro Carvalho Chehab 	}
5599a0bf528SMauro Carvalho Chehab 
56049c44802SMalcolm Priestley 	reg = m88rs2000_readreg(state, 0x70);
56149c44802SMalcolm Priestley 	reg &= 0x7;
56249c44802SMalcolm Priestley 	ret = m88rs2000_writereg(state, 0x70, reg | fec_set);
56349c44802SMalcolm Priestley 
56449c44802SMalcolm Priestley 	ret |= m88rs2000_writereg(state, 0x76, 0x8);
56549c44802SMalcolm Priestley 
56649c44802SMalcolm Priestley 	return ret;
56749c44802SMalcolm Priestley }
5689a0bf528SMauro Carvalho Chehab 
m88rs2000_get_fec(struct m88rs2000_state * state)5690df289a2SMauro Carvalho Chehab static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state)
5709a0bf528SMauro Carvalho Chehab {
5719a0bf528SMauro Carvalho Chehab 	u8 reg;
572b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0x30);
573b858c331SIgor M. Liplianin 	reg = m88rs2000_readreg(state, 0x76);
574b858c331SIgor M. Liplianin 	m88rs2000_writereg(state, 0x9a, 0xb0);
5759a0bf528SMauro Carvalho Chehab 
576a6d8e68bSMalcolm Priestley 	reg &= 0xf0;
577a6d8e68bSMalcolm Priestley 	reg >>= 5;
578a6d8e68bSMalcolm Priestley 
5799a0bf528SMauro Carvalho Chehab 	switch (reg) {
580a6d8e68bSMalcolm Priestley 	case 0x4:
5819a0bf528SMauro Carvalho Chehab 		return FEC_1_2;
582a6d8e68bSMalcolm Priestley 	case 0x3:
5839a0bf528SMauro Carvalho Chehab 		return FEC_2_3;
584a6d8e68bSMalcolm Priestley 	case 0x2:
5859a0bf528SMauro Carvalho Chehab 		return FEC_3_4;
586a6d8e68bSMalcolm Priestley 	case 0x1:
5879a0bf528SMauro Carvalho Chehab 		return FEC_5_6;
588a6d8e68bSMalcolm Priestley 	case 0x0:
5899a0bf528SMauro Carvalho Chehab 		return FEC_7_8;
5909a0bf528SMauro Carvalho Chehab 	default:
5919a0bf528SMauro Carvalho Chehab 		break;
5929a0bf528SMauro Carvalho Chehab 	}
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab 	return FEC_AUTO;
5959a0bf528SMauro Carvalho Chehab }
5969a0bf528SMauro Carvalho Chehab 
m88rs2000_set_frontend(struct dvb_frontend * fe)5979a0bf528SMauro Carvalho Chehab static int m88rs2000_set_frontend(struct dvb_frontend *fe)
5989a0bf528SMauro Carvalho Chehab {
5999a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
6009a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
601cac1c639SColin Ian King 	enum fe_status status = 0;
602b858c331SIgor M. Liplianin 	int i, ret = 0;
603b858c331SIgor M. Liplianin 	u32 tuner_freq;
60406af15d1SMalcolm Priestley 	s16 offset = 0;
6059a0bf528SMauro Carvalho Chehab 	u8 reg;
6069a0bf528SMauro Carvalho Chehab 
6079a0bf528SMauro Carvalho Chehab 	state->no_lock_count = 0;
6089a0bf528SMauro Carvalho Chehab 
6099a0bf528SMauro Carvalho Chehab 	if (c->delivery_system != SYS_DVBS) {
6104bd69e7bSMauro Carvalho Chehab 		deb_info("%s: unsupported delivery system selected (%d)\n",
6119a0bf528SMauro Carvalho Chehab 			 __func__, c->delivery_system);
6129a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
6139a0bf528SMauro Carvalho Chehab 	}
6149a0bf528SMauro Carvalho Chehab 
6159a0bf528SMauro Carvalho Chehab 	/* Set Tuner */
616b858c331SIgor M. Liplianin 	if (fe->ops.tuner_ops.set_params)
617b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.set_params(fe);
618b858c331SIgor M. Liplianin 
6199a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6209a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6219a0bf528SMauro Carvalho Chehab 
62274a6799cSMauro Carvalho Chehab 	if (fe->ops.tuner_ops.get_frequency) {
623b858c331SIgor M. Liplianin 		ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq);
624b858c331SIgor M. Liplianin 
625b858c331SIgor M. Liplianin 		if (ret < 0)
626b858c331SIgor M. Liplianin 			return -ENODEV;
627b858c331SIgor M. Liplianin 
62806af15d1SMalcolm Priestley 		offset = (s16)((s32)tuner_freq - c->frequency);
62974a6799cSMauro Carvalho Chehab 	} else {
63074a6799cSMauro Carvalho Chehab 		offset = 0;
63174a6799cSMauro Carvalho Chehab 	}
632b858c331SIgor M. Liplianin 
63306af15d1SMalcolm Priestley 	/* default mclk value 96.4285 * 2 * 1000 = 192857 */
63406af15d1SMalcolm Priestley 	if (((c->frequency % 192857) >= (192857 - 3000)) ||
63506af15d1SMalcolm Priestley 				(c->frequency % 192857) <= 3000)
63606af15d1SMalcolm Priestley 		ret = m88rs2000_writereg(state, 0x86, 0xc2);
63706af15d1SMalcolm Priestley 	else
63806af15d1SMalcolm Priestley 		ret = m88rs2000_writereg(state, 0x86, 0xc6);
639b858c331SIgor M. Liplianin 
64006af15d1SMalcolm Priestley 	ret |= m88rs2000_set_carrieroffset(fe, offset);
64106af15d1SMalcolm Priestley 	if (ret < 0)
64206af15d1SMalcolm Priestley 		return -ENODEV;
6439a0bf528SMauro Carvalho Chehab 
644dd4491dfSMalcolm Priestley 	/* Reset demod by symbol rate */
645dd4491dfSMalcolm Priestley 	if (c->symbol_rate > 27500000)
646dd4491dfSMalcolm Priestley 		ret = m88rs2000_writereg(state, 0xf1, 0xa4);
647dd4491dfSMalcolm Priestley 	else
648dd4491dfSMalcolm Priestley 		ret = m88rs2000_writereg(state, 0xf1, 0xbf);
649dd4491dfSMalcolm Priestley 
650dd4491dfSMalcolm Priestley 	ret |= m88rs2000_tab_set(state, fe_reset);
6519a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6529a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6539a0bf528SMauro Carvalho Chehab 
6549a0bf528SMauro Carvalho Chehab 	/* Set FEC */
65549c44802SMalcolm Priestley 	ret = m88rs2000_set_fec(state, c->fec_inner);
656b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x85, 0x1);
657b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8a, 0xbf);
658b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x8d, 0x1e);
659b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x90, 0xf1);
660b858c331SIgor M. Liplianin 	ret |= m88rs2000_writereg(state, 0x91, 0x08);
6619a0bf528SMauro Carvalho Chehab 
6629a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6639a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6649a0bf528SMauro Carvalho Chehab 
6659a0bf528SMauro Carvalho Chehab 	/* Set Symbol Rate */
6669a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
6679a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6689a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6699a0bf528SMauro Carvalho Chehab 
6709a0bf528SMauro Carvalho Chehab 	/* Set up Demod */
6719a0bf528SMauro Carvalho Chehab 	ret = m88rs2000_tab_set(state, fe_trigger);
6729a0bf528SMauro Carvalho Chehab 	if (ret < 0)
6739a0bf528SMauro Carvalho Chehab 		return -ENODEV;
6749a0bf528SMauro Carvalho Chehab 
6759a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 25; i++) {
676b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x8c);
6777a9d6b43SMalcolm Priestley 		if ((reg & 0xee) == 0xee) {
6789a0bf528SMauro Carvalho Chehab 			status = FE_HAS_LOCK;
6799a0bf528SMauro Carvalho Chehab 			break;
6809a0bf528SMauro Carvalho Chehab 		}
6819a0bf528SMauro Carvalho Chehab 		state->no_lock_count++;
6829a0bf528SMauro Carvalho Chehab 		if (state->no_lock_count == 15) {
683b858c331SIgor M. Liplianin 			reg = m88rs2000_readreg(state, 0x70);
6849a0bf528SMauro Carvalho Chehab 			reg ^= 0x4;
685b858c331SIgor M. Liplianin 			m88rs2000_writereg(state, 0x70, reg);
6869a0bf528SMauro Carvalho Chehab 			state->no_lock_count = 0;
6879a0bf528SMauro Carvalho Chehab 		}
6889a0bf528SMauro Carvalho Chehab 		msleep(20);
6899a0bf528SMauro Carvalho Chehab 	}
6909a0bf528SMauro Carvalho Chehab 
6919a0bf528SMauro Carvalho Chehab 	if (status & FE_HAS_LOCK) {
6929a0bf528SMauro Carvalho Chehab 		state->fec_inner = m88rs2000_get_fec(state);
693868c9a17SMauro Carvalho Chehab 		/* Unknown suspect SNR level */
694b858c331SIgor M. Liplianin 		reg = m88rs2000_readreg(state, 0x65);
6959a0bf528SMauro Carvalho Chehab 	}
6969a0bf528SMauro Carvalho Chehab 
6979a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = c->frequency;
6989a0bf528SMauro Carvalho Chehab 	state->symbol_rate = c->symbol_rate;
6999a0bf528SMauro Carvalho Chehab 	return 0;
7009a0bf528SMauro Carvalho Chehab }
7019a0bf528SMauro Carvalho Chehab 
m88rs2000_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)7027e3e68bcSMauro Carvalho Chehab static int m88rs2000_get_frontend(struct dvb_frontend *fe,
7037e3e68bcSMauro Carvalho Chehab 				  struct dtv_frontend_properties *c)
7049a0bf528SMauro Carvalho Chehab {
7059a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7067e3e68bcSMauro Carvalho Chehab 
7079a0bf528SMauro Carvalho Chehab 	c->fec_inner = state->fec_inner;
7089a0bf528SMauro Carvalho Chehab 	c->frequency = state->tuner_frequency;
7099a0bf528SMauro Carvalho Chehab 	c->symbol_rate = state->symbol_rate;
7109a0bf528SMauro Carvalho Chehab 	return 0;
7119a0bf528SMauro Carvalho Chehab }
7129a0bf528SMauro Carvalho Chehab 
m88rs2000_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * tune)7138272d0a0SMalcolm Priestley static int m88rs2000_get_tune_settings(struct dvb_frontend *fe,
7148272d0a0SMalcolm Priestley 	struct dvb_frontend_tune_settings *tune)
7158272d0a0SMalcolm Priestley {
7168272d0a0SMalcolm Priestley 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7178272d0a0SMalcolm Priestley 
7188272d0a0SMalcolm Priestley 	if (c->symbol_rate > 3000000)
7198272d0a0SMalcolm Priestley 		tune->min_delay_ms = 2000;
7208272d0a0SMalcolm Priestley 	else
7218272d0a0SMalcolm Priestley 		tune->min_delay_ms = 3000;
7228272d0a0SMalcolm Priestley 
7238272d0a0SMalcolm Priestley 	tune->step_size = c->symbol_rate / 16000;
7248272d0a0SMalcolm Priestley 	tune->max_drift = c->symbol_rate / 2000;
7258272d0a0SMalcolm Priestley 
7268272d0a0SMalcolm Priestley 	return 0;
7278272d0a0SMalcolm Priestley }
7288272d0a0SMalcolm Priestley 
m88rs2000_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)7299a0bf528SMauro Carvalho Chehab static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
7309a0bf528SMauro Carvalho Chehab {
7319a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7329a0bf528SMauro Carvalho Chehab 
7339a0bf528SMauro Carvalho Chehab 	if (enable)
734b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x84);
7359a0bf528SMauro Carvalho Chehab 	else
736b858c331SIgor M. Liplianin 		m88rs2000_writereg(state, 0x81, 0x81);
7379a0bf528SMauro Carvalho Chehab 	udelay(10);
7389a0bf528SMauro Carvalho Chehab 	return 0;
7399a0bf528SMauro Carvalho Chehab }
7409a0bf528SMauro Carvalho Chehab 
m88rs2000_release(struct dvb_frontend * fe)7419a0bf528SMauro Carvalho Chehab static void m88rs2000_release(struct dvb_frontend *fe)
7429a0bf528SMauro Carvalho Chehab {
7439a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = fe->demodulator_priv;
7449a0bf528SMauro Carvalho Chehab 	kfree(state);
7459a0bf528SMauro Carvalho Chehab }
7469a0bf528SMauro Carvalho Chehab 
747bd336e63SMax Kellermann static const struct dvb_frontend_ops m88rs2000_ops = {
7489a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
7499a0bf528SMauro Carvalho Chehab 	.info = {
7509a0bf528SMauro Carvalho Chehab 		.name			= "M88RS2000 DVB-S",
751f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  950 * MHz,
752f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 2150 * MHz,
753f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	= 1 * MHz,
754f1b1eabfSMauro Carvalho Chehab 		.frequency_tolerance_hz	= 5 * MHz,
7559a0bf528SMauro Carvalho Chehab 		.symbol_rate_min	= 1000000,
7569a0bf528SMauro Carvalho Chehab 		.symbol_rate_max	= 45000000,
7579a0bf528SMauro Carvalho Chehab 		.symbol_rate_tolerance	= 500,	/* ppm */
7589a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
7599a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
7603c8023a7SMalcolm Priestley 		      FE_CAN_QPSK | FE_CAN_INVERSION_AUTO |
7619a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_AUTO
7629a0bf528SMauro Carvalho Chehab 	},
7639a0bf528SMauro Carvalho Chehab 
7649a0bf528SMauro Carvalho Chehab 	.release = m88rs2000_release,
7659a0bf528SMauro Carvalho Chehab 	.init = m88rs2000_init,
7669a0bf528SMauro Carvalho Chehab 	.sleep = m88rs2000_sleep,
7679a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
7689a0bf528SMauro Carvalho Chehab 	.read_status = m88rs2000_read_status,
7699a0bf528SMauro Carvalho Chehab 	.read_ber = m88rs2000_read_ber,
7709a0bf528SMauro Carvalho Chehab 	.read_signal_strength = m88rs2000_read_signal_strength,
7719a0bf528SMauro Carvalho Chehab 	.read_snr = m88rs2000_read_snr,
7729a0bf528SMauro Carvalho Chehab 	.read_ucblocks = m88rs2000_read_ucblocks,
7739a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
7749a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = m88rs2000_send_diseqc_burst,
7759a0bf528SMauro Carvalho Chehab 	.set_tone = m88rs2000_set_tone,
7769a0bf528SMauro Carvalho Chehab 	.set_voltage = m88rs2000_set_voltage,
7779a0bf528SMauro Carvalho Chehab 
7789a0bf528SMauro Carvalho Chehab 	.set_frontend = m88rs2000_set_frontend,
7799a0bf528SMauro Carvalho Chehab 	.get_frontend = m88rs2000_get_frontend,
7808272d0a0SMalcolm Priestley 	.get_tune_settings = m88rs2000_get_tune_settings,
7819a0bf528SMauro Carvalho Chehab };
7829a0bf528SMauro Carvalho Chehab 
m88rs2000_attach(const struct m88rs2000_config * config,struct i2c_adapter * i2c)7839a0bf528SMauro Carvalho Chehab struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
7849a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
7859a0bf528SMauro Carvalho Chehab {
7869a0bf528SMauro Carvalho Chehab 	struct m88rs2000_state *state = NULL;
7879a0bf528SMauro Carvalho Chehab 
7889a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
7899a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
7909a0bf528SMauro Carvalho Chehab 	if (state == NULL)
7919a0bf528SMauro Carvalho Chehab 		goto error;
7929a0bf528SMauro Carvalho Chehab 
7939a0bf528SMauro Carvalho Chehab 	/* setup the state */
7949a0bf528SMauro Carvalho Chehab 	state->config = config;
7959a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7969a0bf528SMauro Carvalho Chehab 	state->tuner_frequency = 0;
7979a0bf528SMauro Carvalho Chehab 	state->symbol_rate = 0;
7989a0bf528SMauro Carvalho Chehab 	state->fec_inner = 0;
7999a0bf528SMauro Carvalho Chehab 
8009a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
8019a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &m88rs2000_ops,
8029a0bf528SMauro Carvalho Chehab 			sizeof(struct dvb_frontend_ops));
8039a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
8049a0bf528SMauro Carvalho Chehab 	return &state->frontend;
8059a0bf528SMauro Carvalho Chehab 
8069a0bf528SMauro Carvalho Chehab error:
8079a0bf528SMauro Carvalho Chehab 	kfree(state);
8089a0bf528SMauro Carvalho Chehab 
8099a0bf528SMauro Carvalho Chehab 	return NULL;
8109a0bf528SMauro Carvalho Chehab }
811*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(m88rs2000_attach);
8129a0bf528SMauro Carvalho Chehab 
8139a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
8149a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
8159a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
8169a0bf528SMauro Carvalho Chehab MODULE_VERSION("1.13");
8179a0bf528SMauro Carvalho Chehab 
818