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