19a0bf528SMauro Carvalho Chehab   /*
29a0bf528SMauro Carvalho Chehab      Driver for Philips tda10086 DVBS Demodulator
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab      (c) 2006 Andrew de Quincey
59a0bf528SMauro Carvalho Chehab 
69a0bf528SMauro Carvalho Chehab      This program is free software; you can redistribute it and/or modify
79a0bf528SMauro Carvalho Chehab      it under the terms of the GNU General Public License as published by
89a0bf528SMauro Carvalho Chehab      the Free Software Foundation; either version 2 of the License, or
99a0bf528SMauro Carvalho Chehab      (at your option) any later version.
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab      This program is distributed in the hope that it will be useful,
129a0bf528SMauro Carvalho Chehab      but WITHOUT ANY WARRANTY; without even the implied warranty of
139a0bf528SMauro Carvalho Chehab      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149a0bf528SMauro Carvalho Chehab 
159a0bf528SMauro Carvalho Chehab      GNU General Public License for more details.
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab      You should have received a copy of the GNU General Public License
189a0bf528SMauro Carvalho Chehab      along with this program; if not, write to the Free Software
199a0bf528SMauro Carvalho Chehab      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab    */
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab #include <linux/init.h>
249a0bf528SMauro Carvalho Chehab #include <linux/module.h>
259a0bf528SMauro Carvalho Chehab #include <linux/device.h>
269a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
279a0bf528SMauro Carvalho Chehab #include <linux/string.h>
289a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
319a0bf528SMauro Carvalho Chehab #include "tda10086.h"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab #define SACLK 96000000
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab struct tda10086_state {
369a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
379a0bf528SMauro Carvalho Chehab 	const struct tda10086_config* config;
389a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
399a0bf528SMauro Carvalho Chehab 
409a0bf528SMauro Carvalho Chehab 	/* private demod data */
419a0bf528SMauro Carvalho Chehab 	u32 frequency;
429a0bf528SMauro Carvalho Chehab 	u32 symbol_rate;
439a0bf528SMauro Carvalho Chehab 	bool has_lock;
449a0bf528SMauro Carvalho Chehab };
459a0bf528SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab static int debug;
479a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
489a0bf528SMauro Carvalho Chehab 	do { \
499a0bf528SMauro Carvalho Chehab 		if (debug) printk(KERN_DEBUG "tda10086: " args); \
509a0bf528SMauro Carvalho Chehab 	} while (0)
519a0bf528SMauro Carvalho Chehab 
529a0bf528SMauro Carvalho Chehab static int tda10086_write_byte(struct tda10086_state *state, int reg, int data)
539a0bf528SMauro Carvalho Chehab {
549a0bf528SMauro Carvalho Chehab 	int ret;
559a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg, data };
569a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 };
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab 	msg.addr = state->config->demod_address;
599a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 	if (ret != 1)
629a0bf528SMauro Carvalho Chehab 		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
639a0bf528SMauro Carvalho Chehab 			__func__, reg, data, ret);
649a0bf528SMauro Carvalho Chehab 
659a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? ret : 0;
669a0bf528SMauro Carvalho Chehab }
679a0bf528SMauro Carvalho Chehab 
689a0bf528SMauro Carvalho Chehab static int tda10086_read_byte(struct tda10086_state *state, int reg)
699a0bf528SMauro Carvalho Chehab {
709a0bf528SMauro Carvalho Chehab 	int ret;
719a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
729a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
739a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },
749a0bf528SMauro Carvalho Chehab 				{ .flags = I2C_M_RD, .buf = b1, .len = 1 }};
759a0bf528SMauro Carvalho Chehab 
769a0bf528SMauro Carvalho Chehab 	msg[0].addr = state->config->demod_address;
779a0bf528SMauro Carvalho Chehab 	msg[1].addr = state->config->demod_address;
789a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
799a0bf528SMauro Carvalho Chehab 
809a0bf528SMauro Carvalho Chehab 	if (ret != 2) {
819a0bf528SMauro Carvalho Chehab 		dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg,
829a0bf528SMauro Carvalho Chehab 			ret);
839a0bf528SMauro Carvalho Chehab 		return ret;
849a0bf528SMauro Carvalho Chehab 	}
859a0bf528SMauro Carvalho Chehab 
869a0bf528SMauro Carvalho Chehab 	return b1[0];
879a0bf528SMauro Carvalho Chehab }
889a0bf528SMauro Carvalho Chehab 
899a0bf528SMauro Carvalho Chehab static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data)
909a0bf528SMauro Carvalho Chehab {
919a0bf528SMauro Carvalho Chehab 	int val;
929a0bf528SMauro Carvalho Chehab 
939a0bf528SMauro Carvalho Chehab 	/* read a byte and check */
949a0bf528SMauro Carvalho Chehab 	val = tda10086_read_byte(state, reg);
959a0bf528SMauro Carvalho Chehab 	if (val < 0)
969a0bf528SMauro Carvalho Chehab 		return val;
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab 	/* mask if off */
999a0bf528SMauro Carvalho Chehab 	val = val & ~mask;
1009a0bf528SMauro Carvalho Chehab 	val |= data & 0xff;
1019a0bf528SMauro Carvalho Chehab 
1029a0bf528SMauro Carvalho Chehab 	/* write it out again */
1039a0bf528SMauro Carvalho Chehab 	return tda10086_write_byte(state, reg, val);
1049a0bf528SMauro Carvalho Chehab }
1059a0bf528SMauro Carvalho Chehab 
1069a0bf528SMauro Carvalho Chehab static int tda10086_init(struct dvb_frontend* fe)
1079a0bf528SMauro Carvalho Chehab {
1089a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
1099a0bf528SMauro Carvalho Chehab 	u8 t22k_off = 0x80;
1109a0bf528SMauro Carvalho Chehab 
1119a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
1129a0bf528SMauro Carvalho Chehab 
1139a0bf528SMauro Carvalho Chehab 	if (state->config->diseqc_tone)
1149a0bf528SMauro Carvalho Chehab 		t22k_off = 0;
1159a0bf528SMauro Carvalho Chehab 	/* reset */
1169a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x00, 0x00);
1179a0bf528SMauro Carvalho Chehab 	msleep(10);
1189a0bf528SMauro Carvalho Chehab 
1199a0bf528SMauro Carvalho Chehab 	/* misc setup */
1209a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x01, 0x94);
1219a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x02, 0x35); /* NOTE: TT drivers appear to disable CSWP */
1229a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x03, 0xe4);
1239a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x04, 0x43);
1249a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x0c, 0x0c);
1259a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x1b, 0xb0); /* noise threshold */
1269a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x20, 0x89); /* misc */
1279a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x30, 0x04); /* acquisition period length */
1289a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x32, 0x00); /* irq off */
1299a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x31, 0x56); /* setup AFC */
1309a0bf528SMauro Carvalho Chehab 
1319a0bf528SMauro Carvalho Chehab 	/* setup PLL (this assumes SACLK = 96MHz) */
1329a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x55, 0x2c); /* misc PLL setup */
1339a0bf528SMauro Carvalho Chehab 	if (state->config->xtal_freq == TDA10086_XTAL_16M) {
1349a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x3a, 0x0b); /* M=12 */
1359a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x3b, 0x01); /* P=2 */
1369a0bf528SMauro Carvalho Chehab 	} else {
1379a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x3a, 0x17); /* M=24 */
1389a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x3b, 0x00); /* P=1 */
1399a0bf528SMauro Carvalho Chehab 	}
1409a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x55, 0x20, 0x00); /* powerup PLL */
1419a0bf528SMauro Carvalho Chehab 
1429a0bf528SMauro Carvalho Chehab 	/* setup TS interface */
1439a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x11, 0x81);
1449a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x12, 0x81);
1459a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x19, 0x40); /* parallel mode A + MSBFIRST */
1469a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x56, 0x80); /* powerdown WPLL - unused in the mode we use */
1479a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x57, 0x08); /* bypass WPLL - unused in the mode we use */
1489a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x10, 0x2a);
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab 	/* setup ADC */
1519a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x58, 0x61); /* ADC setup */
1529a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x58, 0x01, 0x00); /* powerup ADC */
1539a0bf528SMauro Carvalho Chehab 
1549a0bf528SMauro Carvalho Chehab 	/* setup AGC */
1559a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x05, 0x0B);
1569a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x37, 0x63);
1579a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x3f, 0x0a); /* NOTE: flydvb varies it */
1589a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x40, 0x64);
1599a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x41, 0x4f);
1609a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x42, 0x43);
1619a0bf528SMauro Carvalho Chehab 
1629a0bf528SMauro Carvalho Chehab 	/* setup viterbi */
1639a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x1a, 0x11); /* VBER 10^6, DVB, QPSK */
1649a0bf528SMauro Carvalho Chehab 
1659a0bf528SMauro Carvalho Chehab 	/* setup carrier recovery */
1669a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x3d, 0x80);
1679a0bf528SMauro Carvalho Chehab 
1689a0bf528SMauro Carvalho Chehab 	/* setup SEC */
1699a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x36, t22k_off); /* all SEC off, 22k tone */
1709a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000)));
1719a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8);
1729a0bf528SMauro Carvalho Chehab 
1739a0bf528SMauro Carvalho Chehab 	return 0;
1749a0bf528SMauro Carvalho Chehab }
1759a0bf528SMauro Carvalho Chehab 
1769a0bf528SMauro Carvalho Chehab static void tda10086_diseqc_wait(struct tda10086_state *state)
1779a0bf528SMauro Carvalho Chehab {
1789a0bf528SMauro Carvalho Chehab 	unsigned long timeout = jiffies + msecs_to_jiffies(200);
1799a0bf528SMauro Carvalho Chehab 	while (!(tda10086_read_byte(state, 0x50) & 0x01)) {
1809a0bf528SMauro Carvalho Chehab 		if(time_after(jiffies, timeout)) {
1819a0bf528SMauro Carvalho Chehab 			printk("%s: diseqc queue not ready, command may be lost.\n", __func__);
1829a0bf528SMauro Carvalho Chehab 			break;
1839a0bf528SMauro Carvalho Chehab 		}
1849a0bf528SMauro Carvalho Chehab 		msleep(10);
1859a0bf528SMauro Carvalho Chehab 	}
1869a0bf528SMauro Carvalho Chehab }
1879a0bf528SMauro Carvalho Chehab 
1880df289a2SMauro Carvalho Chehab static int tda10086_set_tone(struct dvb_frontend *fe,
1890df289a2SMauro Carvalho Chehab 			     enum fe_sec_tone_mode tone)
1909a0bf528SMauro Carvalho Chehab {
1919a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
1929a0bf528SMauro Carvalho Chehab 	u8 t22k_off = 0x80;
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
1959a0bf528SMauro Carvalho Chehab 
1969a0bf528SMauro Carvalho Chehab 	if (state->config->diseqc_tone)
1979a0bf528SMauro Carvalho Chehab 		t22k_off = 0;
1989a0bf528SMauro Carvalho Chehab 
1999a0bf528SMauro Carvalho Chehab 	switch (tone) {
2009a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
2019a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x36, t22k_off);
2029a0bf528SMauro Carvalho Chehab 		break;
2039a0bf528SMauro Carvalho Chehab 
2049a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
2059a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x36, 0x01 + t22k_off);
2069a0bf528SMauro Carvalho Chehab 		break;
2079a0bf528SMauro Carvalho Chehab 	}
2089a0bf528SMauro Carvalho Chehab 
2099a0bf528SMauro Carvalho Chehab 	return 0;
2109a0bf528SMauro Carvalho Chehab }
2119a0bf528SMauro Carvalho Chehab 
2129a0bf528SMauro Carvalho Chehab static int tda10086_send_master_cmd (struct dvb_frontend* fe,
2139a0bf528SMauro Carvalho Chehab 				    struct dvb_diseqc_master_cmd* cmd)
2149a0bf528SMauro Carvalho Chehab {
2159a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
2169a0bf528SMauro Carvalho Chehab 	int i;
2179a0bf528SMauro Carvalho Chehab 	u8 oldval;
2189a0bf528SMauro Carvalho Chehab 	u8 t22k_off = 0x80;
2199a0bf528SMauro Carvalho Chehab 
2209a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
2219a0bf528SMauro Carvalho Chehab 
2229a0bf528SMauro Carvalho Chehab 	if (state->config->diseqc_tone)
2239a0bf528SMauro Carvalho Chehab 		t22k_off = 0;
2249a0bf528SMauro Carvalho Chehab 
2259a0bf528SMauro Carvalho Chehab 	if (cmd->msg_len > 6)
2269a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2279a0bf528SMauro Carvalho Chehab 	oldval = tda10086_read_byte(state, 0x36);
2289a0bf528SMauro Carvalho Chehab 
2299a0bf528SMauro Carvalho Chehab 	for(i=0; i< cmd->msg_len; i++) {
2309a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x48+i, cmd->msg[i]);
2319a0bf528SMauro Carvalho Chehab 	}
2329a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x36, (0x08 + t22k_off)
2339a0bf528SMauro Carvalho Chehab 					| ((cmd->msg_len - 1) << 4));
2349a0bf528SMauro Carvalho Chehab 
2359a0bf528SMauro Carvalho Chehab 	tda10086_diseqc_wait(state);
2369a0bf528SMauro Carvalho Chehab 
2379a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x36, oldval);
2389a0bf528SMauro Carvalho Chehab 
2399a0bf528SMauro Carvalho Chehab 	return 0;
2409a0bf528SMauro Carvalho Chehab }
2419a0bf528SMauro Carvalho Chehab 
2420df289a2SMauro Carvalho Chehab static int tda10086_send_burst(struct dvb_frontend *fe,
2430df289a2SMauro Carvalho Chehab 			       enum fe_sec_mini_cmd minicmd)
2449a0bf528SMauro Carvalho Chehab {
2459a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
2469a0bf528SMauro Carvalho Chehab 	u8 oldval = tda10086_read_byte(state, 0x36);
2479a0bf528SMauro Carvalho Chehab 	u8 t22k_off = 0x80;
2489a0bf528SMauro Carvalho Chehab 
2499a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
2509a0bf528SMauro Carvalho Chehab 
2519a0bf528SMauro Carvalho Chehab 	if (state->config->diseqc_tone)
2529a0bf528SMauro Carvalho Chehab 		t22k_off = 0;
2539a0bf528SMauro Carvalho Chehab 
2549a0bf528SMauro Carvalho Chehab 	switch(minicmd) {
2559a0bf528SMauro Carvalho Chehab 	case SEC_MINI_A:
2569a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x36, 0x04 + t22k_off);
2579a0bf528SMauro Carvalho Chehab 		break;
2589a0bf528SMauro Carvalho Chehab 
2599a0bf528SMauro Carvalho Chehab 	case SEC_MINI_B:
2609a0bf528SMauro Carvalho Chehab 		tda10086_write_byte(state, 0x36, 0x06 + t22k_off);
2619a0bf528SMauro Carvalho Chehab 		break;
2629a0bf528SMauro Carvalho Chehab 	}
2639a0bf528SMauro Carvalho Chehab 
2649a0bf528SMauro Carvalho Chehab 	tda10086_diseqc_wait(state);
2659a0bf528SMauro Carvalho Chehab 
2669a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x36, oldval);
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab 	return 0;
2699a0bf528SMauro Carvalho Chehab }
2709a0bf528SMauro Carvalho Chehab 
2719a0bf528SMauro Carvalho Chehab static int tda10086_set_inversion(struct tda10086_state *state,
2729a0bf528SMauro Carvalho Chehab 				  struct dtv_frontend_properties *fe_params)
2739a0bf528SMauro Carvalho Chehab {
2749a0bf528SMauro Carvalho Chehab 	u8 invval = 0x80;
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	dprintk ("%s %i %i\n", __func__, fe_params->inversion, state->config->invert);
2779a0bf528SMauro Carvalho Chehab 
2789a0bf528SMauro Carvalho Chehab 	switch(fe_params->inversion) {
2799a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
2809a0bf528SMauro Carvalho Chehab 		if (state->config->invert)
2819a0bf528SMauro Carvalho Chehab 			invval = 0x40;
2829a0bf528SMauro Carvalho Chehab 		break;
2839a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
2849a0bf528SMauro Carvalho Chehab 		if (!state->config->invert)
2859a0bf528SMauro Carvalho Chehab 			invval = 0x40;
2869a0bf528SMauro Carvalho Chehab 		break;
2879a0bf528SMauro Carvalho Chehab 	case INVERSION_AUTO:
2889a0bf528SMauro Carvalho Chehab 		invval = 0x00;
2899a0bf528SMauro Carvalho Chehab 		break;
2909a0bf528SMauro Carvalho Chehab 	}
2919a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x0c, 0xc0, invval);
2929a0bf528SMauro Carvalho Chehab 
2939a0bf528SMauro Carvalho Chehab 	return 0;
2949a0bf528SMauro Carvalho Chehab }
2959a0bf528SMauro Carvalho Chehab 
2969a0bf528SMauro Carvalho Chehab static int tda10086_set_symbol_rate(struct tda10086_state *state,
2979a0bf528SMauro Carvalho Chehab 				    struct dtv_frontend_properties *fe_params)
2989a0bf528SMauro Carvalho Chehab {
2999a0bf528SMauro Carvalho Chehab 	u8 dfn = 0;
3009a0bf528SMauro Carvalho Chehab 	u8 afs = 0;
3019a0bf528SMauro Carvalho Chehab 	u8 byp = 0;
3029a0bf528SMauro Carvalho Chehab 	u8 reg37 = 0x43;
3039a0bf528SMauro Carvalho Chehab 	u8 reg42 = 0x43;
3049a0bf528SMauro Carvalho Chehab 	u64 big;
3059a0bf528SMauro Carvalho Chehab 	u32 tmp;
3069a0bf528SMauro Carvalho Chehab 	u32 bdr;
3079a0bf528SMauro Carvalho Chehab 	u32 bdri;
3089a0bf528SMauro Carvalho Chehab 	u32 symbol_rate = fe_params->symbol_rate;
3099a0bf528SMauro Carvalho Chehab 
3109a0bf528SMauro Carvalho Chehab 	dprintk ("%s %i\n", __func__, symbol_rate);
3119a0bf528SMauro Carvalho Chehab 
3129a0bf528SMauro Carvalho Chehab 	/* setup the decimation and anti-aliasing filters.. */
3139a0bf528SMauro Carvalho Chehab 	if (symbol_rate < (u32) (SACLK * 0.0137)) {
3149a0bf528SMauro Carvalho Chehab 		dfn=4;
3159a0bf528SMauro Carvalho Chehab 		afs=1;
3169a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.0208)) {
3179a0bf528SMauro Carvalho Chehab 		dfn=4;
3189a0bf528SMauro Carvalho Chehab 		afs=0;
3199a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.0270)) {
3209a0bf528SMauro Carvalho Chehab 		dfn=3;
3219a0bf528SMauro Carvalho Chehab 		afs=1;
3229a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.0416)) {
3239a0bf528SMauro Carvalho Chehab 		dfn=3;
3249a0bf528SMauro Carvalho Chehab 		afs=0;
3259a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.0550)) {
3269a0bf528SMauro Carvalho Chehab 		dfn=2;
3279a0bf528SMauro Carvalho Chehab 		afs=1;
3289a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.0833)) {
3299a0bf528SMauro Carvalho Chehab 		dfn=2;
3309a0bf528SMauro Carvalho Chehab 		afs=0;
3319a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.1100)) {
3329a0bf528SMauro Carvalho Chehab 		dfn=1;
3339a0bf528SMauro Carvalho Chehab 		afs=1;
3349a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.1666)) {
3359a0bf528SMauro Carvalho Chehab 		dfn=1;
3369a0bf528SMauro Carvalho Chehab 		afs=0;
3379a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.2200)) {
3389a0bf528SMauro Carvalho Chehab 		dfn=0;
3399a0bf528SMauro Carvalho Chehab 		afs=1;
3409a0bf528SMauro Carvalho Chehab 	} else if (symbol_rate < (u32) (SACLK * 0.3333)) {
3419a0bf528SMauro Carvalho Chehab 		dfn=0;
3429a0bf528SMauro Carvalho Chehab 		afs=0;
3439a0bf528SMauro Carvalho Chehab 	} else {
3449a0bf528SMauro Carvalho Chehab 		reg37 = 0x63;
3459a0bf528SMauro Carvalho Chehab 		reg42 = 0x4f;
3469a0bf528SMauro Carvalho Chehab 		byp=1;
3479a0bf528SMauro Carvalho Chehab 	}
3489a0bf528SMauro Carvalho Chehab 
3499a0bf528SMauro Carvalho Chehab 	/* calculate BDR */
3509a0bf528SMauro Carvalho Chehab 	big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn);
3519a0bf528SMauro Carvalho Chehab 	big += ((SACLK/1000ULL)-1ULL);
3529a0bf528SMauro Carvalho Chehab 	do_div(big, (SACLK/1000ULL));
3539a0bf528SMauro Carvalho Chehab 	bdr = big & 0xfffff;
3549a0bf528SMauro Carvalho Chehab 
3559a0bf528SMauro Carvalho Chehab 	/* calculate BDRI */
3569a0bf528SMauro Carvalho Chehab 	tmp = (1<<dfn)*(symbol_rate/1000);
3579a0bf528SMauro Carvalho Chehab 	bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp;
3589a0bf528SMauro Carvalho Chehab 
3599a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x21, (afs << 7) | dfn);
3609a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x20, 0x08, byp << 3);
3619a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x06, bdr);
3629a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x07, bdr >> 8);
3639a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x08, bdr >> 16);
3649a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x09, bdri);
3659a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x37, reg37);
3669a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x42, reg42);
3679a0bf528SMauro Carvalho Chehab 
3689a0bf528SMauro Carvalho Chehab 	return 0;
3699a0bf528SMauro Carvalho Chehab }
3709a0bf528SMauro Carvalho Chehab 
3719a0bf528SMauro Carvalho Chehab static int tda10086_set_fec(struct tda10086_state *state,
3729a0bf528SMauro Carvalho Chehab 			    struct dtv_frontend_properties *fe_params)
3739a0bf528SMauro Carvalho Chehab {
3749a0bf528SMauro Carvalho Chehab 	u8 fecval;
3759a0bf528SMauro Carvalho Chehab 
3769a0bf528SMauro Carvalho Chehab 	dprintk("%s %i\n", __func__, fe_params->fec_inner);
3779a0bf528SMauro Carvalho Chehab 
3789a0bf528SMauro Carvalho Chehab 	switch (fe_params->fec_inner) {
3799a0bf528SMauro Carvalho Chehab 	case FEC_1_2:
3809a0bf528SMauro Carvalho Chehab 		fecval = 0x00;
3819a0bf528SMauro Carvalho Chehab 		break;
3829a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
3839a0bf528SMauro Carvalho Chehab 		fecval = 0x01;
3849a0bf528SMauro Carvalho Chehab 		break;
3859a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
3869a0bf528SMauro Carvalho Chehab 		fecval = 0x02;
3879a0bf528SMauro Carvalho Chehab 		break;
3889a0bf528SMauro Carvalho Chehab 	case FEC_4_5:
3899a0bf528SMauro Carvalho Chehab 		fecval = 0x03;
3909a0bf528SMauro Carvalho Chehab 		break;
3919a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
3929a0bf528SMauro Carvalho Chehab 		fecval = 0x04;
3939a0bf528SMauro Carvalho Chehab 		break;
3949a0bf528SMauro Carvalho Chehab 	case FEC_6_7:
3959a0bf528SMauro Carvalho Chehab 		fecval = 0x05;
3969a0bf528SMauro Carvalho Chehab 		break;
3979a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
3989a0bf528SMauro Carvalho Chehab 		fecval = 0x06;
3999a0bf528SMauro Carvalho Chehab 		break;
4009a0bf528SMauro Carvalho Chehab 	case FEC_8_9:
4019a0bf528SMauro Carvalho Chehab 		fecval = 0x07;
4029a0bf528SMauro Carvalho Chehab 		break;
4039a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
4049a0bf528SMauro Carvalho Chehab 		fecval = 0x08;
4059a0bf528SMauro Carvalho Chehab 		break;
4069a0bf528SMauro Carvalho Chehab 	default:
4079a0bf528SMauro Carvalho Chehab 		return -1;
4089a0bf528SMauro Carvalho Chehab 	}
4099a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x0d, fecval);
4109a0bf528SMauro Carvalho Chehab 
4119a0bf528SMauro Carvalho Chehab 	return 0;
4129a0bf528SMauro Carvalho Chehab }
4139a0bf528SMauro Carvalho Chehab 
4149a0bf528SMauro Carvalho Chehab static int tda10086_set_frontend(struct dvb_frontend *fe)
4159a0bf528SMauro Carvalho Chehab {
4169a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
4179a0bf528SMauro Carvalho Chehab 	struct tda10086_state *state = fe->demodulator_priv;
4189a0bf528SMauro Carvalho Chehab 	int ret;
4199a0bf528SMauro Carvalho Chehab 	u32 freq = 0;
4209a0bf528SMauro Carvalho Chehab 	int freqoff;
4219a0bf528SMauro Carvalho Chehab 
4229a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
4239a0bf528SMauro Carvalho Chehab 
4249a0bf528SMauro Carvalho Chehab 	/* modify parameters for tuning */
4259a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x02, 0x35);
4269a0bf528SMauro Carvalho Chehab 	state->has_lock = false;
4279a0bf528SMauro Carvalho Chehab 
4289a0bf528SMauro Carvalho Chehab 	/* set params */
4299a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
4309a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
4319a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
4329a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
4339a0bf528SMauro Carvalho Chehab 
4349a0bf528SMauro Carvalho Chehab 		if (fe->ops.tuner_ops.get_frequency)
4359a0bf528SMauro Carvalho Chehab 			fe->ops.tuner_ops.get_frequency(fe, &freq);
4369a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
4379a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
4389a0bf528SMauro Carvalho Chehab 	}
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab 	/* calcluate the frequency offset (in *Hz* not kHz) */
4419a0bf528SMauro Carvalho Chehab 	freqoff = fe_params->frequency - freq;
4429a0bf528SMauro Carvalho Chehab 	freqoff = ((1<<16) * freqoff) / (SACLK/1000);
4439a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f));
4449a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x3e, freqoff);
4459a0bf528SMauro Carvalho Chehab 
4469a0bf528SMauro Carvalho Chehab 	if ((ret = tda10086_set_inversion(state, fe_params)) < 0)
4479a0bf528SMauro Carvalho Chehab 		return ret;
4489a0bf528SMauro Carvalho Chehab 	if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0)
4499a0bf528SMauro Carvalho Chehab 		return ret;
4509a0bf528SMauro Carvalho Chehab 	if ((ret = tda10086_set_fec(state, fe_params)) < 0)
4519a0bf528SMauro Carvalho Chehab 		return ret;
4529a0bf528SMauro Carvalho Chehab 
4539a0bf528SMauro Carvalho Chehab 	/* soft reset + disable TS output until lock */
4549a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x10, 0x40, 0x40);
4559a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x00, 0x01, 0x00);
4569a0bf528SMauro Carvalho Chehab 
4579a0bf528SMauro Carvalho Chehab 	state->symbol_rate = fe_params->symbol_rate;
4589a0bf528SMauro Carvalho Chehab 	state->frequency = fe_params->frequency;
4599a0bf528SMauro Carvalho Chehab 	return 0;
4609a0bf528SMauro Carvalho Chehab }
4619a0bf528SMauro Carvalho Chehab 
4627e3e68bcSMauro Carvalho Chehab static int tda10086_get_frontend(struct dvb_frontend *fe,
4637e3e68bcSMauro Carvalho Chehab 				 struct dtv_frontend_properties *fe_params)
4649a0bf528SMauro Carvalho Chehab {
4659a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
4669a0bf528SMauro Carvalho Chehab 	u8 val;
4679a0bf528SMauro Carvalho Chehab 	int tmp;
4689a0bf528SMauro Carvalho Chehab 	u64 tmp64;
4699a0bf528SMauro Carvalho Chehab 
4709a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
4719a0bf528SMauro Carvalho Chehab 
4729a0bf528SMauro Carvalho Chehab 	/* check for invalid symbol rate */
4739a0bf528SMauro Carvalho Chehab 	if (fe_params->symbol_rate < 500000)
4749a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4759a0bf528SMauro Carvalho Chehab 
4769a0bf528SMauro Carvalho Chehab 	/* calculate the updated frequency (note: we convert from Hz->kHz) */
477b674ac29SMauro Carvalho Chehab 	tmp64 = ((u64)tda10086_read_byte(state, 0x52)
478b674ac29SMauro Carvalho Chehab 		| (tda10086_read_byte(state, 0x51) << 8));
4799a0bf528SMauro Carvalho Chehab 	if (tmp64 & 0x8000)
4809a0bf528SMauro Carvalho Chehab 		tmp64 |= 0xffffffffffff0000ULL;
4819a0bf528SMauro Carvalho Chehab 	tmp64 = (tmp64 * (SACLK/1000ULL));
4829a0bf528SMauro Carvalho Chehab 	do_div(tmp64, (1ULL<<15) * (1ULL<<1));
4839a0bf528SMauro Carvalho Chehab 	fe_params->frequency = (int) state->frequency + (int) tmp64;
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 	/* the inversion */
4869a0bf528SMauro Carvalho Chehab 	val = tda10086_read_byte(state, 0x0c);
4879a0bf528SMauro Carvalho Chehab 	if (val & 0x80) {
4889a0bf528SMauro Carvalho Chehab 		switch(val & 0x40) {
4899a0bf528SMauro Carvalho Chehab 		case 0x00:
4909a0bf528SMauro Carvalho Chehab 			fe_params->inversion = INVERSION_OFF;
4919a0bf528SMauro Carvalho Chehab 			if (state->config->invert)
4929a0bf528SMauro Carvalho Chehab 				fe_params->inversion = INVERSION_ON;
4939a0bf528SMauro Carvalho Chehab 			break;
4949a0bf528SMauro Carvalho Chehab 		default:
4959a0bf528SMauro Carvalho Chehab 			fe_params->inversion = INVERSION_ON;
4969a0bf528SMauro Carvalho Chehab 			if (state->config->invert)
4979a0bf528SMauro Carvalho Chehab 				fe_params->inversion = INVERSION_OFF;
4989a0bf528SMauro Carvalho Chehab 			break;
4999a0bf528SMauro Carvalho Chehab 		}
5009a0bf528SMauro Carvalho Chehab 	} else {
5019a0bf528SMauro Carvalho Chehab 		tda10086_read_byte(state, 0x0f);
5029a0bf528SMauro Carvalho Chehab 		switch(val & 0x02) {
5039a0bf528SMauro Carvalho Chehab 		case 0x00:
5049a0bf528SMauro Carvalho Chehab 			fe_params->inversion = INVERSION_OFF;
5059a0bf528SMauro Carvalho Chehab 			if (state->config->invert)
5069a0bf528SMauro Carvalho Chehab 				fe_params->inversion = INVERSION_ON;
5079a0bf528SMauro Carvalho Chehab 			break;
5089a0bf528SMauro Carvalho Chehab 		default:
5099a0bf528SMauro Carvalho Chehab 			fe_params->inversion = INVERSION_ON;
5109a0bf528SMauro Carvalho Chehab 			if (state->config->invert)
5119a0bf528SMauro Carvalho Chehab 				fe_params->inversion = INVERSION_OFF;
5129a0bf528SMauro Carvalho Chehab 			break;
5139a0bf528SMauro Carvalho Chehab 		}
5149a0bf528SMauro Carvalho Chehab 	}
5159a0bf528SMauro Carvalho Chehab 
5169a0bf528SMauro Carvalho Chehab 	/* calculate the updated symbol rate */
5179a0bf528SMauro Carvalho Chehab 	tmp = tda10086_read_byte(state, 0x1d);
5189a0bf528SMauro Carvalho Chehab 	if (tmp & 0x80)
5199a0bf528SMauro Carvalho Chehab 		tmp |= 0xffffff00;
5209a0bf528SMauro Carvalho Chehab 	tmp = (tmp * 480 * (1<<1)) / 128;
5219a0bf528SMauro Carvalho Chehab 	tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000);
5229a0bf528SMauro Carvalho Chehab 	fe_params->symbol_rate = state->symbol_rate + tmp;
5239a0bf528SMauro Carvalho Chehab 
5249a0bf528SMauro Carvalho Chehab 	/* the FEC */
5259a0bf528SMauro Carvalho Chehab 	val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4;
5269a0bf528SMauro Carvalho Chehab 	switch(val) {
5279a0bf528SMauro Carvalho Chehab 	case 0x00:
5289a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_1_2;
5299a0bf528SMauro Carvalho Chehab 		break;
5309a0bf528SMauro Carvalho Chehab 	case 0x01:
5319a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_2_3;
5329a0bf528SMauro Carvalho Chehab 		break;
5339a0bf528SMauro Carvalho Chehab 	case 0x02:
5349a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_3_4;
5359a0bf528SMauro Carvalho Chehab 		break;
5369a0bf528SMauro Carvalho Chehab 	case 0x03:
5379a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_4_5;
5389a0bf528SMauro Carvalho Chehab 		break;
5399a0bf528SMauro Carvalho Chehab 	case 0x04:
5409a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_5_6;
5419a0bf528SMauro Carvalho Chehab 		break;
5429a0bf528SMauro Carvalho Chehab 	case 0x05:
5439a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_6_7;
5449a0bf528SMauro Carvalho Chehab 		break;
5459a0bf528SMauro Carvalho Chehab 	case 0x06:
5469a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_7_8;
5479a0bf528SMauro Carvalho Chehab 		break;
5489a0bf528SMauro Carvalho Chehab 	case 0x07:
5499a0bf528SMauro Carvalho Chehab 		fe_params->fec_inner = FEC_8_9;
5509a0bf528SMauro Carvalho Chehab 		break;
5519a0bf528SMauro Carvalho Chehab 	}
5529a0bf528SMauro Carvalho Chehab 
5539a0bf528SMauro Carvalho Chehab 	return 0;
5549a0bf528SMauro Carvalho Chehab }
5559a0bf528SMauro Carvalho Chehab 
5560df289a2SMauro Carvalho Chehab static int tda10086_read_status(struct dvb_frontend *fe,
5570df289a2SMauro Carvalho Chehab 				enum fe_status *fe_status)
5589a0bf528SMauro Carvalho Chehab {
5599a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
5609a0bf528SMauro Carvalho Chehab 	u8 val;
5619a0bf528SMauro Carvalho Chehab 
5629a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
5639a0bf528SMauro Carvalho Chehab 
5649a0bf528SMauro Carvalho Chehab 	val = tda10086_read_byte(state, 0x0e);
5659a0bf528SMauro Carvalho Chehab 	*fe_status = 0;
5669a0bf528SMauro Carvalho Chehab 	if (val & 0x01)
5679a0bf528SMauro Carvalho Chehab 		*fe_status |= FE_HAS_SIGNAL;
5689a0bf528SMauro Carvalho Chehab 	if (val & 0x02)
5699a0bf528SMauro Carvalho Chehab 		*fe_status |= FE_HAS_CARRIER;
5709a0bf528SMauro Carvalho Chehab 	if (val & 0x04)
5719a0bf528SMauro Carvalho Chehab 		*fe_status |= FE_HAS_VITERBI;
5729a0bf528SMauro Carvalho Chehab 	if (val & 0x08)
5739a0bf528SMauro Carvalho Chehab 		*fe_status |= FE_HAS_SYNC;
5749a0bf528SMauro Carvalho Chehab 	if (val & 0x10) {
5759a0bf528SMauro Carvalho Chehab 		*fe_status |= FE_HAS_LOCK;
5769a0bf528SMauro Carvalho Chehab 		if (!state->has_lock) {
5779a0bf528SMauro Carvalho Chehab 			state->has_lock = true;
5789a0bf528SMauro Carvalho Chehab 			/* modify parameters for stable reception */
5799a0bf528SMauro Carvalho Chehab 			tda10086_write_byte(state, 0x02, 0x00);
5809a0bf528SMauro Carvalho Chehab 		}
5819a0bf528SMauro Carvalho Chehab 	}
5829a0bf528SMauro Carvalho Chehab 
5839a0bf528SMauro Carvalho Chehab 	return 0;
5849a0bf528SMauro Carvalho Chehab }
5859a0bf528SMauro Carvalho Chehab 
5869a0bf528SMauro Carvalho Chehab static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
5879a0bf528SMauro Carvalho Chehab {
5889a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
5899a0bf528SMauro Carvalho Chehab 	u8 _str;
5909a0bf528SMauro Carvalho Chehab 
5919a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
5929a0bf528SMauro Carvalho Chehab 
5939a0bf528SMauro Carvalho Chehab 	_str = 0xff - tda10086_read_byte(state, 0x43);
5949a0bf528SMauro Carvalho Chehab 	*signal = (_str << 8) | _str;
5959a0bf528SMauro Carvalho Chehab 
5969a0bf528SMauro Carvalho Chehab 	return 0;
5979a0bf528SMauro Carvalho Chehab }
5989a0bf528SMauro Carvalho Chehab 
5999a0bf528SMauro Carvalho Chehab static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr)
6009a0bf528SMauro Carvalho Chehab {
6019a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
6029a0bf528SMauro Carvalho Chehab 	u8 _snr;
6039a0bf528SMauro Carvalho Chehab 
6049a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
6059a0bf528SMauro Carvalho Chehab 
6069a0bf528SMauro Carvalho Chehab 	_snr = 0xff - tda10086_read_byte(state, 0x1c);
6079a0bf528SMauro Carvalho Chehab 	*snr = (_snr << 8) | _snr;
6089a0bf528SMauro Carvalho Chehab 
6099a0bf528SMauro Carvalho Chehab 	return 0;
6109a0bf528SMauro Carvalho Chehab }
6119a0bf528SMauro Carvalho Chehab 
6129a0bf528SMauro Carvalho Chehab static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
6139a0bf528SMauro Carvalho Chehab {
6149a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
6159a0bf528SMauro Carvalho Chehab 
6169a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
6179a0bf528SMauro Carvalho Chehab 
6189a0bf528SMauro Carvalho Chehab 	/* read it */
6199a0bf528SMauro Carvalho Chehab 	*ucblocks = tda10086_read_byte(state, 0x18) & 0x7f;
6209a0bf528SMauro Carvalho Chehab 
6219a0bf528SMauro Carvalho Chehab 	/* reset counter */
6229a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x18, 0x00);
6239a0bf528SMauro Carvalho Chehab 	tda10086_write_byte(state, 0x18, 0x80);
6249a0bf528SMauro Carvalho Chehab 
6259a0bf528SMauro Carvalho Chehab 	return 0;
6269a0bf528SMauro Carvalho Chehab }
6279a0bf528SMauro Carvalho Chehab 
6289a0bf528SMauro Carvalho Chehab static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber)
6299a0bf528SMauro Carvalho Chehab {
6309a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
6319a0bf528SMauro Carvalho Chehab 
6329a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
6339a0bf528SMauro Carvalho Chehab 
6349a0bf528SMauro Carvalho Chehab 	/* read it */
6359a0bf528SMauro Carvalho Chehab 	*ber = 0;
6369a0bf528SMauro Carvalho Chehab 	*ber |= tda10086_read_byte(state, 0x15);
6379a0bf528SMauro Carvalho Chehab 	*ber |= tda10086_read_byte(state, 0x16) << 8;
6389a0bf528SMauro Carvalho Chehab 	*ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16;
6399a0bf528SMauro Carvalho Chehab 
6409a0bf528SMauro Carvalho Chehab 	return 0;
6419a0bf528SMauro Carvalho Chehab }
6429a0bf528SMauro Carvalho Chehab 
6439a0bf528SMauro Carvalho Chehab static int tda10086_sleep(struct dvb_frontend* fe)
6449a0bf528SMauro Carvalho Chehab {
6459a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
6469a0bf528SMauro Carvalho Chehab 
6479a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
6489a0bf528SMauro Carvalho Chehab 
6499a0bf528SMauro Carvalho Chehab 	tda10086_write_mask(state, 0x00, 0x08, 0x08);
6509a0bf528SMauro Carvalho Chehab 
6519a0bf528SMauro Carvalho Chehab 	return 0;
6529a0bf528SMauro Carvalho Chehab }
6539a0bf528SMauro Carvalho Chehab 
6549a0bf528SMauro Carvalho Chehab static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
6559a0bf528SMauro Carvalho Chehab {
6569a0bf528SMauro Carvalho Chehab 	struct tda10086_state* state = fe->demodulator_priv;
6579a0bf528SMauro Carvalho Chehab 
6589a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
6599a0bf528SMauro Carvalho Chehab 
6609a0bf528SMauro Carvalho Chehab 	if (enable) {
6619a0bf528SMauro Carvalho Chehab 		tda10086_write_mask(state, 0x00, 0x10, 0x10);
6629a0bf528SMauro Carvalho Chehab 	} else {
6639a0bf528SMauro Carvalho Chehab 		tda10086_write_mask(state, 0x00, 0x10, 0x00);
6649a0bf528SMauro Carvalho Chehab 	}
6659a0bf528SMauro Carvalho Chehab 
6669a0bf528SMauro Carvalho Chehab 	return 0;
6679a0bf528SMauro Carvalho Chehab }
6689a0bf528SMauro Carvalho Chehab 
6699a0bf528SMauro Carvalho Chehab static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
6709a0bf528SMauro Carvalho Chehab {
6719a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
6729a0bf528SMauro Carvalho Chehab 
6739a0bf528SMauro Carvalho Chehab 	if (p->symbol_rate > 20000000) {
6749a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 50;
6759a0bf528SMauro Carvalho Chehab 		fesettings->step_size = 2000;
6769a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 8000;
6779a0bf528SMauro Carvalho Chehab 	} else if (p->symbol_rate > 12000000) {
6789a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 100;
6799a0bf528SMauro Carvalho Chehab 		fesettings->step_size = 1500;
6809a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 9000;
6819a0bf528SMauro Carvalho Chehab 	} else if (p->symbol_rate > 8000000) {
6829a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 100;
6839a0bf528SMauro Carvalho Chehab 		fesettings->step_size = 1000;
6849a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 8000;
6859a0bf528SMauro Carvalho Chehab 	} else if (p->symbol_rate > 4000000) {
6869a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 100;
6879a0bf528SMauro Carvalho Chehab 		fesettings->step_size = 500;
6889a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 7000;
6899a0bf528SMauro Carvalho Chehab 	} else if (p->symbol_rate > 2000000) {
6909a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 200;
6919a0bf528SMauro Carvalho Chehab 		fesettings->step_size = p->symbol_rate / 8000;
6929a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 14 * fesettings->step_size;
6939a0bf528SMauro Carvalho Chehab 	} else {
6949a0bf528SMauro Carvalho Chehab 		fesettings->min_delay_ms = 200;
6959a0bf528SMauro Carvalho Chehab 		fesettings->step_size =  p->symbol_rate / 8000;
6969a0bf528SMauro Carvalho Chehab 		fesettings->max_drift = 18 * fesettings->step_size;
6979a0bf528SMauro Carvalho Chehab 	}
6989a0bf528SMauro Carvalho Chehab 
6999a0bf528SMauro Carvalho Chehab 	return 0;
7009a0bf528SMauro Carvalho Chehab }
7019a0bf528SMauro Carvalho Chehab 
7029a0bf528SMauro Carvalho Chehab static void tda10086_release(struct dvb_frontend* fe)
7039a0bf528SMauro Carvalho Chehab {
7049a0bf528SMauro Carvalho Chehab 	struct tda10086_state *state = fe->demodulator_priv;
7059a0bf528SMauro Carvalho Chehab 	tda10086_sleep(fe);
7069a0bf528SMauro Carvalho Chehab 	kfree(state);
7079a0bf528SMauro Carvalho Chehab }
7089a0bf528SMauro Carvalho Chehab 
7099a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops tda10086_ops = {
7109a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
7119a0bf528SMauro Carvalho Chehab 	.info = {
7129a0bf528SMauro Carvalho Chehab 		.name     = "Philips TDA10086 DVB-S",
7139a0bf528SMauro Carvalho Chehab 		.frequency_min    = 950000,
7149a0bf528SMauro Carvalho Chehab 		.frequency_max    = 2150000,
7159a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 125,     /* kHz for QPSK frontends */
7169a0bf528SMauro Carvalho Chehab 		.symbol_rate_min  = 1000000,
7179a0bf528SMauro Carvalho Chehab 		.symbol_rate_max  = 45000000,
7189a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
7199a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
7209a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
7219a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK
7229a0bf528SMauro Carvalho Chehab 	},
7239a0bf528SMauro Carvalho Chehab 
7249a0bf528SMauro Carvalho Chehab 	.release = tda10086_release,
7259a0bf528SMauro Carvalho Chehab 
7269a0bf528SMauro Carvalho Chehab 	.init = tda10086_init,
7279a0bf528SMauro Carvalho Chehab 	.sleep = tda10086_sleep,
7289a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = tda10086_i2c_gate_ctrl,
7299a0bf528SMauro Carvalho Chehab 
7309a0bf528SMauro Carvalho Chehab 	.set_frontend = tda10086_set_frontend,
7319a0bf528SMauro Carvalho Chehab 	.get_frontend = tda10086_get_frontend,
7329a0bf528SMauro Carvalho Chehab 	.get_tune_settings = tda10086_get_tune_settings,
7339a0bf528SMauro Carvalho Chehab 
7349a0bf528SMauro Carvalho Chehab 	.read_status = tda10086_read_status,
7359a0bf528SMauro Carvalho Chehab 	.read_ber = tda10086_read_ber,
7369a0bf528SMauro Carvalho Chehab 	.read_signal_strength = tda10086_read_signal_strength,
7379a0bf528SMauro Carvalho Chehab 	.read_snr = tda10086_read_snr,
7389a0bf528SMauro Carvalho Chehab 	.read_ucblocks = tda10086_read_ucblocks,
7399a0bf528SMauro Carvalho Chehab 
7409a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = tda10086_send_master_cmd,
7419a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = tda10086_send_burst,
7429a0bf528SMauro Carvalho Chehab 	.set_tone = tda10086_set_tone,
7439a0bf528SMauro Carvalho Chehab };
7449a0bf528SMauro Carvalho Chehab 
7459a0bf528SMauro Carvalho Chehab struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
7469a0bf528SMauro Carvalho Chehab 				     struct i2c_adapter* i2c)
7479a0bf528SMauro Carvalho Chehab {
7489a0bf528SMauro Carvalho Chehab 	struct tda10086_state *state;
7499a0bf528SMauro Carvalho Chehab 
7509a0bf528SMauro Carvalho Chehab 	dprintk ("%s\n", __func__);
7519a0bf528SMauro Carvalho Chehab 
7529a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
7539a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL);
7549a0bf528SMauro Carvalho Chehab 	if (!state)
7559a0bf528SMauro Carvalho Chehab 		return NULL;
7569a0bf528SMauro Carvalho Chehab 
7579a0bf528SMauro Carvalho Chehab 	/* setup the state */
7589a0bf528SMauro Carvalho Chehab 	state->config = config;
7599a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
7609a0bf528SMauro Carvalho Chehab 
7619a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
7629a0bf528SMauro Carvalho Chehab 	if (tda10086_read_byte(state, 0x1e) != 0xe1) {
7639a0bf528SMauro Carvalho Chehab 		kfree(state);
7649a0bf528SMauro Carvalho Chehab 		return NULL;
7659a0bf528SMauro Carvalho Chehab 	}
7669a0bf528SMauro Carvalho Chehab 
7679a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
7689a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops));
7699a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
7709a0bf528SMauro Carvalho Chehab 	return &state->frontend;
7719a0bf528SMauro Carvalho Chehab }
7729a0bf528SMauro Carvalho Chehab 
7739a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
7749a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
7759a0bf528SMauro Carvalho Chehab 
7769a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator");
7779a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Andrew de Quincey");
7789a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
7799a0bf528SMauro Carvalho Chehab 
7809a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(tda10086_attach);
781