19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     cx24110 - Single Chip Satellite Channel Receiver driver module
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
59a0bf528SMauro Carvalho Chehab     work
69a0bf528SMauro Carvalho Chehab     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
79a0bf528SMauro Carvalho Chehab 
89a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
99a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
109a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
119a0bf528SMauro Carvalho Chehab     (at your option) any later version.
129a0bf528SMauro Carvalho Chehab 
139a0bf528SMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
149a0bf528SMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
159a0bf528SMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
189a0bf528SMauro Carvalho Chehab 
199a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
209a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
219a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab */
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
269a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
279a0bf528SMauro Carvalho Chehab #include <linux/module.h>
289a0bf528SMauro Carvalho Chehab #include <linux/init.h>
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
319a0bf528SMauro Carvalho Chehab #include "cx24110.h"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab struct cx24110_state {
359a0bf528SMauro Carvalho Chehab 
369a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
379a0bf528SMauro Carvalho Chehab 
389a0bf528SMauro Carvalho Chehab 	const struct cx24110_config* config;
399a0bf528SMauro Carvalho Chehab 
409a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
419a0bf528SMauro Carvalho Chehab 
429a0bf528SMauro Carvalho Chehab 	u32 lastber;
439a0bf528SMauro Carvalho Chehab 	u32 lastbler;
449a0bf528SMauro Carvalho Chehab 	u32 lastesn0;
459a0bf528SMauro Carvalho Chehab };
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static int debug;
489a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
499a0bf528SMauro Carvalho Chehab 	do { \
509a0bf528SMauro Carvalho Chehab 		if (debug) printk(KERN_DEBUG "cx24110: " args); \
519a0bf528SMauro Carvalho Chehab 	} while (0)
529a0bf528SMauro Carvalho Chehab 
539a0bf528SMauro Carvalho Chehab static struct {u8 reg; u8 data;} cx24110_regdata[]=
549a0bf528SMauro Carvalho Chehab 		      /* Comments beginning with @ denote this value should
559a0bf528SMauro Carvalho Chehab 			 be the default */
569a0bf528SMauro Carvalho Chehab 	{{0x09,0x01}, /* SoftResetAll */
579a0bf528SMauro Carvalho Chehab 	 {0x09,0x00}, /* release reset */
589a0bf528SMauro Carvalho Chehab 	 {0x01,0xe8}, /* MSB of code rate 27.5MS/s */
599a0bf528SMauro Carvalho Chehab 	 {0x02,0x17}, /* middle byte " */
609a0bf528SMauro Carvalho Chehab 	 {0x03,0x29}, /* LSB         " */
619a0bf528SMauro Carvalho Chehab 	 {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */
629a0bf528SMauro Carvalho Chehab 	 {0x06,0xa5}, /* @ PLL 60MHz */
639a0bf528SMauro Carvalho Chehab 	 {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */
649a0bf528SMauro Carvalho Chehab 	 {0x0a,0x00}, /* @ partial chip disables, do not set */
659a0bf528SMauro Carvalho Chehab 	 {0x0b,0x01}, /* set output clock in gapped mode, start signal low
669a0bf528SMauro Carvalho Chehab 			 active for first byte */
679a0bf528SMauro Carvalho Chehab 	 {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */
689a0bf528SMauro Carvalho Chehab 	 {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */
699a0bf528SMauro Carvalho Chehab 	 {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1
709a0bf528SMauro Carvalho Chehab 			 to avoid starting the BER counter. Reset the
719a0bf528SMauro Carvalho Chehab 			 CRC test bit. Finite counting selected */
729a0bf528SMauro Carvalho Chehab 	 {0x15,0xff}, /* @ size of the limited time window for RS BER
739a0bf528SMauro Carvalho Chehab 			 estimation. It is <value>*256 RS blocks, this
749a0bf528SMauro Carvalho Chehab 			 gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */
759a0bf528SMauro Carvalho Chehab 	 {0x16,0x00}, /* @ enable all RS output ports */
769a0bf528SMauro Carvalho Chehab 	 {0x17,0x04}, /* @ time window allowed for the RS to sync */
779a0bf528SMauro Carvalho Chehab 	 {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned
789a0bf528SMauro Carvalho Chehab 			 for automatically */
799a0bf528SMauro Carvalho Chehab 		      /* leave the current code rate and normalization
809a0bf528SMauro Carvalho Chehab 			 registers as they are after reset... */
819a0bf528SMauro Carvalho Chehab 	 {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting
829a0bf528SMauro Carvalho Chehab 			 only once */
839a0bf528SMauro Carvalho Chehab 	 {0x23,0x18}, /* @ size of the limited time window for Viterbi BER
849a0bf528SMauro Carvalho Chehab 			 estimation. It is <value>*65536 channel bits, i.e.
859a0bf528SMauro Carvalho Chehab 			 approx. 38ms at 27.5MS/s, rate 3/4 */
869a0bf528SMauro Carvalho Chehab 	 {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */
879a0bf528SMauro Carvalho Chehab 		      /* leave front-end AGC parameters at default values */
889a0bf528SMauro Carvalho Chehab 		      /* leave decimation AGC parameters at default values */
899a0bf528SMauro Carvalho Chehab 	 {0x35,0x40}, /* disable all interrupts. They are not connected anyway */
909a0bf528SMauro Carvalho Chehab 	 {0x36,0xff}, /* clear all interrupt pending flags */
919a0bf528SMauro Carvalho Chehab 	 {0x37,0x00}, /* @ fully enable AutoAcqq state machine */
929a0bf528SMauro Carvalho Chehab 	 {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */
939a0bf528SMauro Carvalho Chehab 		      /* leave the equalizer parameters on their default values */
949a0bf528SMauro Carvalho Chehab 		      /* leave the final AGC parameters on their default values */
959a0bf528SMauro Carvalho Chehab 	 {0x41,0x00}, /* @ MSB of front-end derotator frequency */
969a0bf528SMauro Carvalho Chehab 	 {0x42,0x00}, /* @ middle bytes " */
979a0bf528SMauro Carvalho Chehab 	 {0x43,0x00}, /* @ LSB          " */
989a0bf528SMauro Carvalho Chehab 		      /* leave the carrier tracking loop parameters on default */
999a0bf528SMauro Carvalho Chehab 		      /* leave the bit timing loop parameters at default */
1009a0bf528SMauro Carvalho Chehab 	 {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
1019a0bf528SMauro Carvalho Chehab 		      /* the cx24108 data sheet for symbol rates above 15MS/s */
1029a0bf528SMauro Carvalho Chehab 	 {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
1039a0bf528SMauro Carvalho Chehab 	 {0x61,0x95}, /* GPIO pins 1-4 have special function */
1049a0bf528SMauro Carvalho Chehab 	 {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */
1059a0bf528SMauro Carvalho Chehab 	 {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */
1069a0bf528SMauro Carvalho Chehab 	 {0x64,0x20}, /* GPIO 6 is input, all others are outputs */
1079a0bf528SMauro Carvalho Chehab 	 {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */
1089a0bf528SMauro Carvalho Chehab 	 {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */
1099a0bf528SMauro Carvalho Chehab 	 {0x73,0x00}, /* @ disable several demod bypasses */
1109a0bf528SMauro Carvalho Chehab 	 {0x74,0x00}, /* @  " */
1119a0bf528SMauro Carvalho Chehab 	 {0x75,0x00}  /* @  " */
1129a0bf528SMauro Carvalho Chehab 		      /* the remaining registers are for SEC */
1139a0bf528SMauro Carvalho Chehab 	};
1149a0bf528SMauro Carvalho Chehab 
1159a0bf528SMauro Carvalho Chehab 
1169a0bf528SMauro Carvalho Chehab static int cx24110_writereg (struct cx24110_state* state, int reg, int data)
1179a0bf528SMauro Carvalho Chehab {
1189a0bf528SMauro Carvalho Chehab 	u8 buf [] = { reg, data };
1199a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
1209a0bf528SMauro Carvalho Chehab 	int err;
1219a0bf528SMauro Carvalho Chehab 
1229a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
1239a0bf528SMauro Carvalho Chehab 		dprintk ("%s: writereg error (err == %i, reg == 0x%02x,"
1249a0bf528SMauro Carvalho Chehab 			 " data == 0x%02x)\n", __func__, err, reg, data);
1259a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1269a0bf528SMauro Carvalho Chehab 	}
1279a0bf528SMauro Carvalho Chehab 
1289a0bf528SMauro Carvalho Chehab 	return 0;
1299a0bf528SMauro Carvalho Chehab }
1309a0bf528SMauro Carvalho Chehab 
1319a0bf528SMauro Carvalho Chehab static int cx24110_readreg (struct cx24110_state* state, u8 reg)
1329a0bf528SMauro Carvalho Chehab {
1339a0bf528SMauro Carvalho Chehab 	int ret;
1349a0bf528SMauro Carvalho Chehab 	u8 b0 [] = { reg };
1359a0bf528SMauro Carvalho Chehab 	u8 b1 [] = { 0 };
1369a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
1379a0bf528SMauro Carvalho Chehab 			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
1389a0bf528SMauro Carvalho Chehab 
1399a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
1409a0bf528SMauro Carvalho Chehab 
1419a0bf528SMauro Carvalho Chehab 	if (ret != 2) return ret;
1429a0bf528SMauro Carvalho Chehab 
1439a0bf528SMauro Carvalho Chehab 	return b1[0];
1449a0bf528SMauro Carvalho Chehab }
1459a0bf528SMauro Carvalho Chehab 
1469a0bf528SMauro Carvalho Chehab static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
1479a0bf528SMauro Carvalho Chehab {
1489a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab 	switch (inversion) {
1519a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
1529a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
1539a0bf528SMauro Carvalho Chehab 		/* AcqSpectrInvDis on. No idea why someone should want this */
1549a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7);
1559a0bf528SMauro Carvalho Chehab 		/* Initial value 0 at start of acq */
1569a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef);
1579a0bf528SMauro Carvalho Chehab 		/* current value 0 */
1589a0bf528SMauro Carvalho Chehab 		/* The cx24110 manual tells us this reg is read-only.
1599a0bf528SMauro Carvalho Chehab 		   But what the heck... set it ayways */
1609a0bf528SMauro Carvalho Chehab 		break;
1619a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
1629a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
1639a0bf528SMauro Carvalho Chehab 		/* AcqSpectrInvDis on. No idea why someone should want this */
1649a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08);
1659a0bf528SMauro Carvalho Chehab 		/* Initial value 1 at start of acq */
1669a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10);
1679a0bf528SMauro Carvalho Chehab 		/* current value 1 */
1689a0bf528SMauro Carvalho Chehab 		break;
1699a0bf528SMauro Carvalho Chehab 	case INVERSION_AUTO:
1709a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe);
1719a0bf528SMauro Carvalho Chehab 		/* AcqSpectrInvDis off. Leave initial & current states as is */
1729a0bf528SMauro Carvalho Chehab 		break;
1739a0bf528SMauro Carvalho Chehab 	default:
1749a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1759a0bf528SMauro Carvalho Chehab 	}
1769a0bf528SMauro Carvalho Chehab 
1779a0bf528SMauro Carvalho Chehab 	return 0;
1789a0bf528SMauro Carvalho Chehab }
1799a0bf528SMauro Carvalho Chehab 
1809a0bf528SMauro Carvalho Chehab static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
1819a0bf528SMauro Carvalho Chehab {
1829a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */
1839a0bf528SMauro Carvalho Chehab 
1849a0bf528SMauro Carvalho Chehab 	static const int rate[]={-1,1,2,3,5,7,-1};
1859a0bf528SMauro Carvalho Chehab 	static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
1869a0bf528SMauro Carvalho Chehab 	static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
1879a0bf528SMauro Carvalho Chehab 
1889a0bf528SMauro Carvalho Chehab 	/* Well, the AutoAcq engine of the cx24106 and 24110 automatically
1899a0bf528SMauro Carvalho Chehab 	   searches all enabled viterbi rates, and can handle non-standard
1909a0bf528SMauro Carvalho Chehab 	   rates as well. */
1919a0bf528SMauro Carvalho Chehab 
1929a0bf528SMauro Carvalho Chehab 	if (fec>FEC_AUTO)
1939a0bf528SMauro Carvalho Chehab 		fec=FEC_AUTO;
1949a0bf528SMauro Carvalho Chehab 
1959a0bf528SMauro Carvalho Chehab 	if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
1969a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
1979a0bf528SMauro Carvalho Chehab 		/* clear AcqVitDis bit */
1989a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x18,0xae);
1999a0bf528SMauro Carvalho Chehab 		/* allow all DVB standard code rates */
2009a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
2019a0bf528SMauro Carvalho Chehab 		/* set nominal Viterbi rate 3/4 */
2029a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
2039a0bf528SMauro Carvalho Chehab 		/* set current Viterbi rate 3/4 */
2049a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
2059a0bf528SMauro Carvalho Chehab 		/* set the puncture registers for code rate 3/4 */
2069a0bf528SMauro Carvalho Chehab 		return 0;
2079a0bf528SMauro Carvalho Chehab 	} else {
2089a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
2099a0bf528SMauro Carvalho Chehab 		/* set AcqVitDis bit */
2109a0bf528SMauro Carvalho Chehab 		if(rate[fec]>0) {
2119a0bf528SMauro Carvalho Chehab 			cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
2129a0bf528SMauro Carvalho Chehab 			/* set nominal Viterbi rate */
2139a0bf528SMauro Carvalho Chehab 			cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
2149a0bf528SMauro Carvalho Chehab 			/* set current Viterbi rate */
2159a0bf528SMauro Carvalho Chehab 			cx24110_writereg(state,0x1a,g1[fec]);
2169a0bf528SMauro Carvalho Chehab 			cx24110_writereg(state,0x1b,g2[fec]);
2179a0bf528SMauro Carvalho Chehab 			/* not sure if this is the right way: I always used AutoAcq mode */
2189a0bf528SMauro Carvalho Chehab 	   } else
2199a0bf528SMauro Carvalho Chehab 		   return -EOPNOTSUPP;
2209a0bf528SMauro Carvalho Chehab /* fixme (low): which is the correct return code? */
221c2c1b415SPeter Senna Tschudin 	}
2229a0bf528SMauro Carvalho Chehab 	return 0;
2239a0bf528SMauro Carvalho Chehab }
2249a0bf528SMauro Carvalho Chehab 
2259a0bf528SMauro Carvalho Chehab static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
2269a0bf528SMauro Carvalho Chehab {
2279a0bf528SMauro Carvalho Chehab 	int i;
2289a0bf528SMauro Carvalho Chehab 
2299a0bf528SMauro Carvalho Chehab 	i=cx24110_readreg(state,0x22)&0x0f;
2309a0bf528SMauro Carvalho Chehab 	if(!(i&0x08)) {
2319a0bf528SMauro Carvalho Chehab 		return FEC_1_2 + i - 1;
2329a0bf528SMauro Carvalho Chehab 	} else {
2339a0bf528SMauro Carvalho Chehab /* fixme (low): a special code rate has been selected. In theory, we need to
2349a0bf528SMauro Carvalho Chehab    return a denominator value, a numerator value, and a pair of puncture
2359a0bf528SMauro Carvalho Chehab    maps to correctly describe this mode. But this should never happen in
2369a0bf528SMauro Carvalho Chehab    practice, because it cannot be set by cx24110_get_fec. */
2379a0bf528SMauro Carvalho Chehab 	   return FEC_NONE;
2389a0bf528SMauro Carvalho Chehab 	}
2399a0bf528SMauro Carvalho Chehab }
2409a0bf528SMauro Carvalho Chehab 
2419a0bf528SMauro Carvalho Chehab static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
2429a0bf528SMauro Carvalho Chehab {
2439a0bf528SMauro Carvalho Chehab /* fixme (low): add error handling */
2449a0bf528SMauro Carvalho Chehab 	u32 ratio;
2459a0bf528SMauro Carvalho Chehab 	u32 tmp, fclk, BDRI;
2469a0bf528SMauro Carvalho Chehab 
2479a0bf528SMauro Carvalho Chehab 	static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
2489a0bf528SMauro Carvalho Chehab 	int i;
2499a0bf528SMauro Carvalho Chehab 
2509a0bf528SMauro Carvalho Chehab 	dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate);
2519a0bf528SMauro Carvalho Chehab 	if (srate>90999000UL/2)
2529a0bf528SMauro Carvalho Chehab 		srate=90999000UL/2;
2539a0bf528SMauro Carvalho Chehab 	if (srate<500000)
2549a0bf528SMauro Carvalho Chehab 		srate=500000;
2559a0bf528SMauro Carvalho Chehab 
2569a0bf528SMauro Carvalho Chehab 	for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++)
2579a0bf528SMauro Carvalho Chehab 		;
2589a0bf528SMauro Carvalho Chehab 	/* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
2599a0bf528SMauro Carvalho Chehab 	   and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
2609a0bf528SMauro Carvalho Chehab 	   R06[3:0] PLLphaseDetGain */
2619a0bf528SMauro Carvalho Chehab 	tmp=cx24110_readreg(state,0x07)&0xfc;
2629a0bf528SMauro Carvalho Chehab 	if(srate<90999000UL/4) { /* sample rate 45MHz*/
2639a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x07,tmp);
2649a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x06,0x78);
2659a0bf528SMauro Carvalho Chehab 		fclk=90999000UL/2;
2669a0bf528SMauro Carvalho Chehab 	} else if(srate<60666000UL/2) { /* sample rate 60MHz */
2679a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x07,tmp|0x1);
2689a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x06,0xa5);
2699a0bf528SMauro Carvalho Chehab 		fclk=60666000UL;
2709a0bf528SMauro Carvalho Chehab 	} else if(srate<80888000UL/2) { /* sample rate 80MHz */
2719a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x07,tmp|0x2);
2729a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x06,0x87);
2739a0bf528SMauro Carvalho Chehab 		fclk=80888000UL;
2749a0bf528SMauro Carvalho Chehab 	} else { /* sample rate 90MHz */
2759a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x07,tmp|0x3);
2769a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x06,0x78);
2779a0bf528SMauro Carvalho Chehab 		fclk=90999000UL;
278c2c1b415SPeter Senna Tschudin 	}
2799a0bf528SMauro Carvalho Chehab 	dprintk("cx24110 debug: fclk %d Hz\n",fclk);
2809a0bf528SMauro Carvalho Chehab 	/* we need to divide two integers with approx. 27 bits in 32 bit
2819a0bf528SMauro Carvalho Chehab 	   arithmetic giving a 25 bit result */
2829a0bf528SMauro Carvalho Chehab 	/* the maximum dividend is 90999000/2, 0x02b6446c, this number is
2839a0bf528SMauro Carvalho Chehab 	   also the most complex divisor. Hence, the dividend has,
2849a0bf528SMauro Carvalho Chehab 	   assuming 32bit unsigned arithmetic, 6 clear bits on top, the
2859a0bf528SMauro Carvalho Chehab 	   divisor 2 unused bits at the bottom. Also, the quotient is
2869a0bf528SMauro Carvalho Chehab 	   always less than 1/2. Borrowed from VES1893.c, of course */
2879a0bf528SMauro Carvalho Chehab 
2889a0bf528SMauro Carvalho Chehab 	tmp=srate<<6;
2899a0bf528SMauro Carvalho Chehab 	BDRI=fclk>>2;
2909a0bf528SMauro Carvalho Chehab 	ratio=(tmp/BDRI);
2919a0bf528SMauro Carvalho Chehab 
2929a0bf528SMauro Carvalho Chehab 	tmp=(tmp%BDRI)<<8;
2939a0bf528SMauro Carvalho Chehab 	ratio=(ratio<<8)+(tmp/BDRI);
2949a0bf528SMauro Carvalho Chehab 
2959a0bf528SMauro Carvalho Chehab 	tmp=(tmp%BDRI)<<8;
2969a0bf528SMauro Carvalho Chehab 	ratio=(ratio<<8)+(tmp/BDRI);
2979a0bf528SMauro Carvalho Chehab 
2989a0bf528SMauro Carvalho Chehab 	tmp=(tmp%BDRI)<<1;
2999a0bf528SMauro Carvalho Chehab 	ratio=(ratio<<1)+(tmp/BDRI);
3009a0bf528SMauro Carvalho Chehab 
3019a0bf528SMauro Carvalho Chehab 	dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]);
3029a0bf528SMauro Carvalho Chehab 	dprintk("fclk = %d\n", fclk);
3039a0bf528SMauro Carvalho Chehab 	dprintk("ratio= %08x\n", ratio);
3049a0bf528SMauro Carvalho Chehab 
3059a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state, 0x1, (ratio>>16)&0xff);
3069a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state, 0x2, (ratio>>8)&0xff);
3079a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state, 0x3, (ratio)&0xff);
3089a0bf528SMauro Carvalho Chehab 
3099a0bf528SMauro Carvalho Chehab 	return 0;
3109a0bf528SMauro Carvalho Chehab 
3119a0bf528SMauro Carvalho Chehab }
3129a0bf528SMauro Carvalho Chehab 
3139a0bf528SMauro Carvalho Chehab static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len)
3149a0bf528SMauro Carvalho Chehab {
3159a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
3169a0bf528SMauro Carvalho Chehab 
3179a0bf528SMauro Carvalho Chehab 	if (len != 3)
3189a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3199a0bf528SMauro Carvalho Chehab 
3209a0bf528SMauro Carvalho Chehab /* tuner data is 21 bits long, must be left-aligned in data */
3219a0bf528SMauro Carvalho Chehab /* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
3229a0bf528SMauro Carvalho Chehab /* FIXME (low): add error handling, avoid infinite loops if HW fails... */
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
3259a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
3269a0bf528SMauro Carvalho Chehab 
3279a0bf528SMauro Carvalho Chehab 	/* if the auto tuner writer is still busy, clear it out */
3289a0bf528SMauro Carvalho Chehab 	while (cx24110_readreg(state,0x6d)&0x80)
3299a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x72,0);
3309a0bf528SMauro Carvalho Chehab 
3319a0bf528SMauro Carvalho Chehab 	/* write the topmost 8 bits */
3329a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x72,buf[0]);
3339a0bf528SMauro Carvalho Chehab 
3349a0bf528SMauro Carvalho Chehab 	/* wait for the send to be completed */
3359a0bf528SMauro Carvalho Chehab 	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3369a0bf528SMauro Carvalho Chehab 		;
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab 	/* send another 8 bytes */
3399a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x72,buf[1]);
3409a0bf528SMauro Carvalho Chehab 	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3419a0bf528SMauro Carvalho Chehab 		;
3429a0bf528SMauro Carvalho Chehab 
3439a0bf528SMauro Carvalho Chehab 	/* and the topmost 5 bits of this byte */
3449a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x72,buf[2]);
3459a0bf528SMauro Carvalho Chehab 	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3469a0bf528SMauro Carvalho Chehab 		;
3479a0bf528SMauro Carvalho Chehab 
3489a0bf528SMauro Carvalho Chehab 	/* now strobe the enable line once */
3499a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x6d,0x32);
3509a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x6d,0x30);
3519a0bf528SMauro Carvalho Chehab 
3529a0bf528SMauro Carvalho Chehab 	return 0;
3539a0bf528SMauro Carvalho Chehab }
3549a0bf528SMauro Carvalho Chehab 
3559a0bf528SMauro Carvalho Chehab static int cx24110_initfe(struct dvb_frontend* fe)
3569a0bf528SMauro Carvalho Chehab {
3579a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
3589a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */
3599a0bf528SMauro Carvalho Chehab 	int i;
3609a0bf528SMauro Carvalho Chehab 
3619a0bf528SMauro Carvalho Chehab 	dprintk("%s: init chip\n", __func__);
3629a0bf528SMauro Carvalho Chehab 
3639a0bf528SMauro Carvalho Chehab 	for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) {
3649a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
365c2c1b415SPeter Senna Tschudin 	}
3669a0bf528SMauro Carvalho Chehab 
3679a0bf528SMauro Carvalho Chehab 	return 0;
3689a0bf528SMauro Carvalho Chehab }
3699a0bf528SMauro Carvalho Chehab 
3709a0bf528SMauro Carvalho Chehab static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
3719a0bf528SMauro Carvalho Chehab {
3729a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
3739a0bf528SMauro Carvalho Chehab 
3749a0bf528SMauro Carvalho Chehab 	switch (voltage) {
3759a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
3769a0bf528SMauro Carvalho Chehab 		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0);
3779a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
3789a0bf528SMauro Carvalho Chehab 		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
3799a0bf528SMauro Carvalho Chehab 	default:
3809a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3819a0bf528SMauro Carvalho Chehab 	};
3829a0bf528SMauro Carvalho Chehab }
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
3859a0bf528SMauro Carvalho Chehab {
3869a0bf528SMauro Carvalho Chehab 	int rv, bit;
3879a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
3889a0bf528SMauro Carvalho Chehab 	unsigned long timeout;
3899a0bf528SMauro Carvalho Chehab 
3909a0bf528SMauro Carvalho Chehab 	if (burst == SEC_MINI_A)
3919a0bf528SMauro Carvalho Chehab 		bit = 0x00;
3929a0bf528SMauro Carvalho Chehab 	else if (burst == SEC_MINI_B)
3939a0bf528SMauro Carvalho Chehab 		bit = 0x08;
3949a0bf528SMauro Carvalho Chehab 	else
3959a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab 	rv = cx24110_readreg(state, 0x77);
3989a0bf528SMauro Carvalho Chehab 	if (!(rv & 0x04))
3999a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state, 0x77, rv | 0x04);
4009a0bf528SMauro Carvalho Chehab 
4019a0bf528SMauro Carvalho Chehab 	rv = cx24110_readreg(state, 0x76);
4029a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit));
4039a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(100);
4049a0bf528SMauro Carvalho Chehab 	while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40))
4059a0bf528SMauro Carvalho Chehab 		; /* wait for LNB ready */
4069a0bf528SMauro Carvalho Chehab 
4079a0bf528SMauro Carvalho Chehab 	return 0;
4089a0bf528SMauro Carvalho Chehab }
4099a0bf528SMauro Carvalho Chehab 
4109a0bf528SMauro Carvalho Chehab static int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
4119a0bf528SMauro Carvalho Chehab 				   struct dvb_diseqc_master_cmd *cmd)
4129a0bf528SMauro Carvalho Chehab {
4139a0bf528SMauro Carvalho Chehab 	int i, rv;
4149a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
4159a0bf528SMauro Carvalho Chehab 	unsigned long timeout;
4169a0bf528SMauro Carvalho Chehab 
4179a0bf528SMauro Carvalho Chehab 	if (cmd->msg_len < 3 || cmd->msg_len > 6)
4189a0bf528SMauro Carvalho Chehab 		return -EINVAL;  /* not implemented */
4199a0bf528SMauro Carvalho Chehab 
4209a0bf528SMauro Carvalho Chehab 	for (i = 0; i < cmd->msg_len; i++)
4219a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state, 0x79 + i, cmd->msg[i]);
4229a0bf528SMauro Carvalho Chehab 
4239a0bf528SMauro Carvalho Chehab 	rv = cx24110_readreg(state, 0x77);
4249a0bf528SMauro Carvalho Chehab 	if (rv & 0x04) {
4259a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state, 0x77, rv & ~0x04);
4269a0bf528SMauro Carvalho Chehab 		msleep(30); /* reportedly fixes switching problems */
4279a0bf528SMauro Carvalho Chehab 	}
4289a0bf528SMauro Carvalho Chehab 
4299a0bf528SMauro Carvalho Chehab 	rv = cx24110_readreg(state, 0x76);
4309a0bf528SMauro Carvalho Chehab 
4319a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
4329a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(100);
4339a0bf528SMauro Carvalho Chehab 	while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40))
4349a0bf528SMauro Carvalho Chehab 		; /* wait for LNB ready */
4359a0bf528SMauro Carvalho Chehab 
4369a0bf528SMauro Carvalho Chehab 	return 0;
4379a0bf528SMauro Carvalho Chehab }
4389a0bf528SMauro Carvalho Chehab 
4399a0bf528SMauro Carvalho Chehab static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
4409a0bf528SMauro Carvalho Chehab {
4419a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
4429a0bf528SMauro Carvalho Chehab 
4439a0bf528SMauro Carvalho Chehab 	int sync = cx24110_readreg (state, 0x55);
4449a0bf528SMauro Carvalho Chehab 
4459a0bf528SMauro Carvalho Chehab 	*status = 0;
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 	if (sync & 0x10)
4489a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
4499a0bf528SMauro Carvalho Chehab 
4509a0bf528SMauro Carvalho Chehab 	if (sync & 0x08)
4519a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
4529a0bf528SMauro Carvalho Chehab 
4539a0bf528SMauro Carvalho Chehab 	sync = cx24110_readreg (state, 0x08);
4549a0bf528SMauro Carvalho Chehab 
4559a0bf528SMauro Carvalho Chehab 	if (sync & 0x40)
4569a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
4579a0bf528SMauro Carvalho Chehab 
4589a0bf528SMauro Carvalho Chehab 	if (sync & 0x20)
4599a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
4609a0bf528SMauro Carvalho Chehab 
4619a0bf528SMauro Carvalho Chehab 	if ((sync & 0x60) == 0x60)
4629a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
4639a0bf528SMauro Carvalho Chehab 
4649a0bf528SMauro Carvalho Chehab 	return 0;
4659a0bf528SMauro Carvalho Chehab }
4669a0bf528SMauro Carvalho Chehab 
4679a0bf528SMauro Carvalho Chehab static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber)
4689a0bf528SMauro Carvalho Chehab {
4699a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
4709a0bf528SMauro Carvalho Chehab 
4719a0bf528SMauro Carvalho Chehab 	/* fixme (maybe): value range is 16 bit. Scale? */
4729a0bf528SMauro Carvalho Chehab 	if(cx24110_readreg(state,0x24)&0x10) {
4739a0bf528SMauro Carvalho Chehab 		/* the Viterbi error counter has finished one counting window */
4749a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x24,0x04); /* select the ber reg */
4759a0bf528SMauro Carvalho Chehab 		state->lastber=cx24110_readreg(state,0x25)|
4769a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state,0x26)<<8);
4779a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x24,0x04); /* start new count window */
4789a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x24,0x14);
4799a0bf528SMauro Carvalho Chehab 	}
4809a0bf528SMauro Carvalho Chehab 	*ber = state->lastber;
4819a0bf528SMauro Carvalho Chehab 
4829a0bf528SMauro Carvalho Chehab 	return 0;
4839a0bf528SMauro Carvalho Chehab }
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
4869a0bf528SMauro Carvalho Chehab {
4879a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
4889a0bf528SMauro Carvalho Chehab 
4899a0bf528SMauro Carvalho Chehab /* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */
4909a0bf528SMauro Carvalho Chehab 	u8 signal = cx24110_readreg (state, 0x27)+128;
4919a0bf528SMauro Carvalho Chehab 	*signal_strength = (signal << 8) | signal;
4929a0bf528SMauro Carvalho Chehab 
4939a0bf528SMauro Carvalho Chehab 	return 0;
4949a0bf528SMauro Carvalho Chehab }
4959a0bf528SMauro Carvalho Chehab 
4969a0bf528SMauro Carvalho Chehab static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
4979a0bf528SMauro Carvalho Chehab {
4989a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 	/* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */
5019a0bf528SMauro Carvalho Chehab 	if(cx24110_readreg(state,0x6a)&0x80) {
5029a0bf528SMauro Carvalho Chehab 		/* the Es/N0 error counter has finished one counting window */
5039a0bf528SMauro Carvalho Chehab 		state->lastesn0=cx24110_readreg(state,0x69)|
5049a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state,0x68)<<8);
5059a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x6a,0x84); /* start new count window */
5069a0bf528SMauro Carvalho Chehab 	}
5079a0bf528SMauro Carvalho Chehab 	*snr = state->lastesn0;
5089a0bf528SMauro Carvalho Chehab 
5099a0bf528SMauro Carvalho Chehab 	return 0;
5109a0bf528SMauro Carvalho Chehab }
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
5139a0bf528SMauro Carvalho Chehab {
5149a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
5159a0bf528SMauro Carvalho Chehab 
5169a0bf528SMauro Carvalho Chehab 	if(cx24110_readreg(state,0x10)&0x40) {
5179a0bf528SMauro Carvalho Chehab 		/* the RS error counter has finished one counting window */
5189a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x10,0x60); /* select the byer reg */
5199a0bf528SMauro Carvalho Chehab 		(void)(cx24110_readreg(state, 0x12) |
5209a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state, 0x13) << 8) |
5219a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state, 0x14) << 16));
5229a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x10,0x70); /* select the bler reg */
5239a0bf528SMauro Carvalho Chehab 		state->lastbler=cx24110_readreg(state,0x12)|
5249a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state,0x13)<<8)|
5259a0bf528SMauro Carvalho Chehab 			(cx24110_readreg(state,0x14)<<16);
5269a0bf528SMauro Carvalho Chehab 		cx24110_writereg(state,0x10,0x20); /* start new count window */
5279a0bf528SMauro Carvalho Chehab 	}
5289a0bf528SMauro Carvalho Chehab 	*ucblocks = state->lastbler;
5299a0bf528SMauro Carvalho Chehab 
5309a0bf528SMauro Carvalho Chehab 	return 0;
5319a0bf528SMauro Carvalho Chehab }
5329a0bf528SMauro Carvalho Chehab 
5339a0bf528SMauro Carvalho Chehab static int cx24110_set_frontend(struct dvb_frontend *fe)
5349a0bf528SMauro Carvalho Chehab {
5359a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
5369a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
5379a0bf528SMauro Carvalho Chehab 
5389a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
5399a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
5409a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
5419a0bf528SMauro Carvalho Chehab 	}
5429a0bf528SMauro Carvalho Chehab 
5439a0bf528SMauro Carvalho Chehab 	cx24110_set_inversion(state, p->inversion);
5449a0bf528SMauro Carvalho Chehab 	cx24110_set_fec(state, p->fec_inner);
5459a0bf528SMauro Carvalho Chehab 	cx24110_set_symbolrate(state, p->symbol_rate);
5469a0bf528SMauro Carvalho Chehab 	cx24110_writereg(state,0x04,0x05); /* start acquisition */
5479a0bf528SMauro Carvalho Chehab 
5489a0bf528SMauro Carvalho Chehab 	return 0;
5499a0bf528SMauro Carvalho Chehab }
5509a0bf528SMauro Carvalho Chehab 
5519a0bf528SMauro Carvalho Chehab static int cx24110_get_frontend(struct dvb_frontend *fe)
5529a0bf528SMauro Carvalho Chehab {
5539a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
5549a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
5559a0bf528SMauro Carvalho Chehab 	s32 afc; unsigned sclk;
5569a0bf528SMauro Carvalho Chehab 
5579a0bf528SMauro Carvalho Chehab /* cannot read back tuner settings (freq). Need to have some private storage */
5589a0bf528SMauro Carvalho Chehab 
5599a0bf528SMauro Carvalho Chehab 	sclk = cx24110_readreg (state, 0x07) & 0x03;
5609a0bf528SMauro Carvalho Chehab /* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz.
5619a0bf528SMauro Carvalho Chehab  * Need 64 bit arithmetic. Is thiss possible in the kernel? */
5629a0bf528SMauro Carvalho Chehab 	if (sclk==0) sclk=90999000L/2L;
5639a0bf528SMauro Carvalho Chehab 	else if (sclk==1) sclk=60666000L;
5649a0bf528SMauro Carvalho Chehab 	else if (sclk==2) sclk=80888000L;
5659a0bf528SMauro Carvalho Chehab 	else sclk=90999000L;
5669a0bf528SMauro Carvalho Chehab 	sclk>>=8;
5679a0bf528SMauro Carvalho Chehab 	afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+
5689a0bf528SMauro Carvalho Chehab 	      ((sclk*cx24110_readreg (state, 0x45))>>8)+
5699a0bf528SMauro Carvalho Chehab 	      ((sclk*cx24110_readreg (state, 0x46))>>16);
5709a0bf528SMauro Carvalho Chehab 
5719a0bf528SMauro Carvalho Chehab 	p->frequency += afc;
5729a0bf528SMauro Carvalho Chehab 	p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ?
5739a0bf528SMauro Carvalho Chehab 				INVERSION_ON : INVERSION_OFF;
5749a0bf528SMauro Carvalho Chehab 	p->fec_inner = cx24110_get_fec(state);
5759a0bf528SMauro Carvalho Chehab 
5769a0bf528SMauro Carvalho Chehab 	return 0;
5779a0bf528SMauro Carvalho Chehab }
5789a0bf528SMauro Carvalho Chehab 
5799a0bf528SMauro Carvalho Chehab static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
5809a0bf528SMauro Carvalho Chehab {
5819a0bf528SMauro Carvalho Chehab 	struct cx24110_state *state = fe->demodulator_priv;
5829a0bf528SMauro Carvalho Chehab 
5839a0bf528SMauro Carvalho Chehab 	return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0));
5849a0bf528SMauro Carvalho Chehab }
5859a0bf528SMauro Carvalho Chehab 
5869a0bf528SMauro Carvalho Chehab static void cx24110_release(struct dvb_frontend* fe)
5879a0bf528SMauro Carvalho Chehab {
5889a0bf528SMauro Carvalho Chehab 	struct cx24110_state* state = fe->demodulator_priv;
5899a0bf528SMauro Carvalho Chehab 	kfree(state);
5909a0bf528SMauro Carvalho Chehab }
5919a0bf528SMauro Carvalho Chehab 
5929a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24110_ops;
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
5959a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter* i2c)
5969a0bf528SMauro Carvalho Chehab {
5979a0bf528SMauro Carvalho Chehab 	struct cx24110_state* state = NULL;
5989a0bf528SMauro Carvalho Chehab 	int ret;
5999a0bf528SMauro Carvalho Chehab 
6009a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
6019a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL);
6029a0bf528SMauro Carvalho Chehab 	if (state == NULL) goto error;
6039a0bf528SMauro Carvalho Chehab 
6049a0bf528SMauro Carvalho Chehab 	/* setup the state */
6059a0bf528SMauro Carvalho Chehab 	state->config = config;
6069a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
6079a0bf528SMauro Carvalho Chehab 	state->lastber = 0;
6089a0bf528SMauro Carvalho Chehab 	state->lastbler = 0;
6099a0bf528SMauro Carvalho Chehab 	state->lastesn0 = 0;
6109a0bf528SMauro Carvalho Chehab 
6119a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
6129a0bf528SMauro Carvalho Chehab 	ret = cx24110_readreg(state, 0x00);
6139a0bf528SMauro Carvalho Chehab 	if ((ret != 0x5a) && (ret != 0x69)) goto error;
6149a0bf528SMauro Carvalho Chehab 
6159a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
6169a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops));
6179a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
6189a0bf528SMauro Carvalho Chehab 	return &state->frontend;
6199a0bf528SMauro Carvalho Chehab 
6209a0bf528SMauro Carvalho Chehab error:
6219a0bf528SMauro Carvalho Chehab 	kfree(state);
6229a0bf528SMauro Carvalho Chehab 	return NULL;
6239a0bf528SMauro Carvalho Chehab }
6249a0bf528SMauro Carvalho Chehab 
6259a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24110_ops = {
6269a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
6279a0bf528SMauro Carvalho Chehab 	.info = {
6289a0bf528SMauro Carvalho Chehab 		.name = "Conexant CX24110 DVB-S",
6299a0bf528SMauro Carvalho Chehab 		.frequency_min = 950000,
6309a0bf528SMauro Carvalho Chehab 		.frequency_max = 2150000,
6319a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 1011,  /* kHz for QPSK frontends */
6329a0bf528SMauro Carvalho Chehab 		.frequency_tolerance = 29500,
6339a0bf528SMauro Carvalho Chehab 		.symbol_rate_min = 1000000,
6349a0bf528SMauro Carvalho Chehab 		.symbol_rate_max = 45000000,
6359a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
6369a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
6379a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
6389a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK | FE_CAN_RECOVER
6399a0bf528SMauro Carvalho Chehab 	},
6409a0bf528SMauro Carvalho Chehab 
6419a0bf528SMauro Carvalho Chehab 	.release = cx24110_release,
6429a0bf528SMauro Carvalho Chehab 
6439a0bf528SMauro Carvalho Chehab 	.init = cx24110_initfe,
6449a0bf528SMauro Carvalho Chehab 	.write = _cx24110_pll_write,
6459a0bf528SMauro Carvalho Chehab 	.set_frontend = cx24110_set_frontend,
6469a0bf528SMauro Carvalho Chehab 	.get_frontend = cx24110_get_frontend,
6479a0bf528SMauro Carvalho Chehab 	.read_status = cx24110_read_status,
6489a0bf528SMauro Carvalho Chehab 	.read_ber = cx24110_read_ber,
6499a0bf528SMauro Carvalho Chehab 	.read_signal_strength = cx24110_read_signal_strength,
6509a0bf528SMauro Carvalho Chehab 	.read_snr = cx24110_read_snr,
6519a0bf528SMauro Carvalho Chehab 	.read_ucblocks = cx24110_read_ucblocks,
6529a0bf528SMauro Carvalho Chehab 
6539a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = cx24110_send_diseqc_msg,
6549a0bf528SMauro Carvalho Chehab 	.set_tone = cx24110_set_tone,
6559a0bf528SMauro Carvalho Chehab 	.set_voltage = cx24110_set_voltage,
6569a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = cx24110_diseqc_send_burst,
6579a0bf528SMauro Carvalho Chehab };
6589a0bf528SMauro Carvalho Chehab 
6599a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
6609a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
6619a0bf528SMauro Carvalho Chehab 
6629a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
6639a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Peter Hettkamp");
6649a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
6659a0bf528SMauro Carvalho Chehab 
6669a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(cx24110_attach);
667