xref: /linux/drivers/media/dvb-frontends/cx24116.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20d46748cSSteven Toth /*
30d46748cSSteven Toth     Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
40d46748cSSteven Toth 
50d46748cSSteven Toth     Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
6490c8684SDarron Broad     Copyright (C) 2006-2007 Georg Acher
7490c8684SDarron Broad     Copyright (C) 2007-2008 Darron Broad
8490c8684SDarron Broad 	March 2007
9490c8684SDarron Broad 	    Fixed some bugs.
10490c8684SDarron Broad 	    Added diseqc support.
11490c8684SDarron Broad 	    Added corrected signal strength support.
12490c8684SDarron Broad 	August 2007
13490c8684SDarron Broad 	    Sync with legacy version.
14490c8684SDarron Broad 	    Some clean ups.
15490c8684SDarron Broad     Copyright (C) 2008 Igor Liplianin
16490c8684SDarron Broad 	September, 9th 2008
17490c8684SDarron Broad 	    Fixed locking on high symbol rates (>30000).
18c063a489SIgor M. Liplianin 	    Implement MPEG initialization parameter.
19c7bdcd0fSIgor M. Liplianin 	January, 17th 2009
20c7bdcd0fSIgor M. Liplianin 	    Fill set_voltage with actually control voltage code.
21c7bdcd0fSIgor M. Liplianin 	    Correct set tone to not affect voltage.
220d46748cSSteven Toth 
230d46748cSSteven Toth */
240d46748cSSteven Toth 
250d46748cSSteven Toth #include <linux/slab.h>
260d46748cSSteven Toth #include <linux/kernel.h>
270d46748cSSteven Toth #include <linux/module.h>
280d46748cSSteven Toth #include <linux/moduleparam.h>
290d46748cSSteven Toth #include <linux/init.h>
300d46748cSSteven Toth #include <linux/firmware.h>
310d46748cSSteven Toth 
32fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
330d46748cSSteven Toth #include "cx24116.h"
340d46748cSSteven Toth 
35f11ec7d4SSteven Toth static int debug;
36f11ec7d4SSteven Toth module_param(debug, int, 0644);
37f11ec7d4SSteven Toth MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
38f11ec7d4SSteven Toth 
39490c8684SDarron Broad #define dprintk(args...) \
40490c8684SDarron Broad 	do { \
41f11ec7d4SSteven Toth 		if (debug) \
4298c94823SSteven Toth 			printk(KERN_INFO "cx24116: " args); \
43490c8684SDarron Broad 	} while (0)
44490c8684SDarron Broad 
450d46748cSSteven Toth #define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
460d46748cSSteven Toth #define CX24116_SEARCH_RANGE_KHZ 5000
470d46748cSSteven Toth 
48490c8684SDarron Broad /* known registers */
49490c8684SDarron Broad #define CX24116_REG_COMMAND (0x00)      /* command args 0x00..0x1e */
50490c8684SDarron Broad #define CX24116_REG_EXECUTE (0x1f)      /* execute command */
51490c8684SDarron Broad #define CX24116_REG_MAILBOX (0x96)      /* FW or multipurpose mailbox? */
52490c8684SDarron Broad #define CX24116_REG_RESET   (0x20)      /* reset status > 0     */
53490c8684SDarron Broad #define CX24116_REG_SIGNAL  (0x9e)      /* signal low           */
54490c8684SDarron Broad #define CX24116_REG_SSTATUS (0x9d)      /* signal high / status */
558953db79SSteven Toth #define CX24116_REG_QUALITY8 (0xa3)
56490c8684SDarron Broad #define CX24116_REG_QSTATUS (0xbc)
578953db79SSteven Toth #define CX24116_REG_QUALITY0 (0xd5)
58490c8684SDarron Broad #define CX24116_REG_BER0    (0xc9)
59490c8684SDarron Broad #define CX24116_REG_BER8    (0xc8)
60490c8684SDarron Broad #define CX24116_REG_BER16   (0xc7)
61490c8684SDarron Broad #define CX24116_REG_BER24   (0xc6)
62490c8684SDarron Broad #define CX24116_REG_UCB0    (0xcb)
63490c8684SDarron Broad #define CX24116_REG_UCB8    (0xca)
64490c8684SDarron Broad #define CX24116_REG_CLKDIV  (0xf3)
65490c8684SDarron Broad #define CX24116_REG_RATEDIV (0xf9)
6698c94823SSteven Toth 
6798c94823SSteven Toth /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
6898c94823SSteven Toth #define CX24116_REG_FECSTATUS (0x9c)
69681faa0bSDarron Broad 
70681faa0bSDarron Broad /* FECSTATUS bits */
7198c94823SSteven Toth /* mask to determine configured fec (not tuned) or actual fec (tuned) */
7298c94823SSteven Toth #define CX24116_FEC_FECMASK   (0x1f)
7398c94823SSteven Toth 
7498c94823SSteven Toth /* Select DVB-S demodulator, else DVB-S2 */
7598c94823SSteven Toth #define CX24116_FEC_DVBS      (0x20)
76681faa0bSDarron Broad #define CX24116_FEC_UNKNOWN   (0x40)    /* Unknown/unused */
7798c94823SSteven Toth 
7898c94823SSteven Toth /* Pilot mode requested when tuning else always reset when tuned */
7998c94823SSteven Toth #define CX24116_FEC_PILOT     (0x80)
800d46748cSSteven Toth 
810d46748cSSteven Toth /* arg buffer size */
820d46748cSSteven Toth #define CX24116_ARGLEN (0x1e)
830d46748cSSteven Toth 
84490c8684SDarron Broad /* rolloff */
85490c8684SDarron Broad #define CX24116_ROLLOFF_020 (0x00)
86490c8684SDarron Broad #define CX24116_ROLLOFF_025 (0x01)
87490c8684SDarron Broad #define CX24116_ROLLOFF_035 (0x02)
88490c8684SDarron Broad 
89490c8684SDarron Broad /* pilot bit */
9001a8f038SDarron Broad #define CX24116_PILOT_OFF (0x00)
9101a8f038SDarron Broad #define CX24116_PILOT_ON (0x40)
92490c8684SDarron Broad 
93490c8684SDarron Broad /* signal status */
94490c8684SDarron Broad #define CX24116_HAS_SIGNAL   (0x01)
95490c8684SDarron Broad #define CX24116_HAS_CARRIER  (0x02)
96490c8684SDarron Broad #define CX24116_HAS_VITERBI  (0x04)
97490c8684SDarron Broad #define CX24116_HAS_SYNCLOCK (0x08)
98490c8684SDarron Broad #define CX24116_HAS_UNKNOWN1 (0x10)
99490c8684SDarron Broad #define CX24116_HAS_UNKNOWN2 (0x20)
1006639f1e0SDarron Broad #define CX24116_STATUS_MASK  (0x0f)
101490c8684SDarron Broad #define CX24116_SIGNAL_MASK  (0xc0)
102490c8684SDarron Broad 
103490c8684SDarron Broad #define CX24116_DISEQC_TONEOFF   (0)    /* toneburst never sent */
104490c8684SDarron Broad #define CX24116_DISEQC_TONECACHE (1)    /* toneburst cached     */
105490c8684SDarron Broad #define CX24116_DISEQC_MESGCACHE (2)    /* message cached       */
106490c8684SDarron Broad 
1070d46748cSSteven Toth /* arg offset for DiSEqC */
1080d46748cSSteven Toth #define CX24116_DISEQC_BURST  (1)
1090d46748cSSteven Toth #define CX24116_DISEQC_ARG2_2 (2)   /* unknown value=2 */
1100d46748cSSteven Toth #define CX24116_DISEQC_ARG3_0 (3)   /* unknown value=0 */
1110d46748cSSteven Toth #define CX24116_DISEQC_ARG4_0 (4)   /* unknown value=0 */
1120d46748cSSteven Toth #define CX24116_DISEQC_MSGLEN (5)
1130d46748cSSteven Toth #define CX24116_DISEQC_MSGOFS (6)
1140d46748cSSteven Toth 
1150d46748cSSteven Toth /* DiSEqC burst */
1160d46748cSSteven Toth #define CX24116_DISEQC_MINI_A (0)
1170d46748cSSteven Toth #define CX24116_DISEQC_MINI_B (1)
1180d46748cSSteven Toth 
119490c8684SDarron Broad /* DiSEqC tone burst */
120490c8684SDarron Broad static int toneburst = 1;
121f11ec7d4SSteven Toth module_param(toneburst, int, 0644);
12298c94823SSteven Toth MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\
12398c94823SSteven Toth 	"2=MESSAGE CACHE (default:1)");
124490c8684SDarron Broad 
1258953db79SSteven Toth /* SNR measurements */
126f11ec7d4SSteven Toth static int esno_snr;
127f11ec7d4SSteven Toth module_param(esno_snr, int, 0644);
12853f1fe94SHans Petter Selasky MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\
12998c94823SSteven Toth 	"1=ESNO(db * 10) (default:0)");
1308953db79SSteven Toth 
131f11ec7d4SSteven Toth enum cmds {
132490c8684SDarron Broad 	CMD_SET_VCO     = 0x10,
1330d46748cSSteven Toth 	CMD_TUNEREQUEST = 0x11,
134490c8684SDarron Broad 	CMD_MPEGCONFIG  = 0x13,
135490c8684SDarron Broad 	CMD_TUNERINIT   = 0x14,
136490c8684SDarron Broad 	CMD_BANDWIDTH   = 0x15,
137490c8684SDarron Broad 	CMD_GETAGC      = 0x19,
138490c8684SDarron Broad 	CMD_LNBCONFIG   = 0x20,
139490c8684SDarron Broad 	CMD_LNBSEND     = 0x21, /* Formerly CMD_SEND_DISEQC */
140c7bdcd0fSIgor M. Liplianin 	CMD_LNBDCLEVEL  = 0x22,
1410d46748cSSteven Toth 	CMD_SET_TONE    = 0x23,
142490c8684SDarron Broad 	CMD_UPDFWVERS   = 0x35,
143490c8684SDarron Broad 	CMD_TUNERSLEEP  = 0x36,
144490c8684SDarron Broad 	CMD_AGCCONTROL  = 0x3b, /* Unknown */
1450d46748cSSteven Toth };
1460d46748cSSteven Toth 
1470d46748cSSteven Toth /* The Demod/Tuner can't easily provide these, we cache them */
148f11ec7d4SSteven Toth struct cx24116_tuning {
1490d46748cSSteven Toth 	u32 frequency;
1500d46748cSSteven Toth 	u32 symbol_rate;
1510df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion;
1520df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
1530d46748cSSteven Toth 
1540df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
1550df289a2SMauro Carvalho Chehab 	enum fe_modulation modulation;
1560df289a2SMauro Carvalho Chehab 	enum fe_pilot pilot;
1570df289a2SMauro Carvalho Chehab 	enum fe_rolloff rolloff;
1580d46748cSSteven Toth 
1590d46748cSSteven Toth 	/* Demod values */
1600d46748cSSteven Toth 	u8 fec_val;
1610d46748cSSteven Toth 	u8 fec_mask;
1620d46748cSSteven Toth 	u8 inversion_val;
16301a8f038SDarron Broad 	u8 pilot_val;
164490c8684SDarron Broad 	u8 rolloff_val;
1650d46748cSSteven Toth };
1660d46748cSSteven Toth 
1670d46748cSSteven Toth /* Basic commands that are sent to the firmware */
168f11ec7d4SSteven Toth struct cx24116_cmd {
1690d46748cSSteven Toth 	u8 len;
1700d46748cSSteven Toth 	u8 args[CX24116_ARGLEN];
1710d46748cSSteven Toth };
1720d46748cSSteven Toth 
173f11ec7d4SSteven Toth struct cx24116_state {
1740d46748cSSteven Toth 	struct i2c_adapter *i2c;
1750d46748cSSteven Toth 	const struct cx24116_config *config;
1760d46748cSSteven Toth 
1770d46748cSSteven Toth 	struct dvb_frontend frontend;
1780d46748cSSteven Toth 
1790d46748cSSteven Toth 	struct cx24116_tuning dcur;
1800d46748cSSteven Toth 	struct cx24116_tuning dnxt;
1810d46748cSSteven Toth 
1820d46748cSSteven Toth 	u8 skip_fw_load;
1830d46748cSSteven Toth 	u8 burst;
184490c8684SDarron Broad 	struct cx24116_cmd dsec_cmd;
1850d46748cSSteven Toth };
1860d46748cSSteven Toth 
1870d46748cSSteven Toth static int cx24116_writereg(struct cx24116_state *state, int reg, int data)
1880d46748cSSteven Toth {
1890d46748cSSteven Toth 	u8 buf[] = { reg, data };
1900d46748cSSteven Toth 	struct i2c_msg msg = { .addr = state->config->demod_address,
1910d46748cSSteven Toth 		.flags = 0, .buf = buf, .len = 2 };
1920d46748cSSteven Toth 	int err;
1930d46748cSSteven Toth 
1940d46748cSSteven Toth 	if (debug > 1)
1950d46748cSSteven Toth 		printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
1960d46748cSSteven Toth 			__func__, reg, data);
1970d46748cSSteven Toth 
198f11ec7d4SSteven Toth 	err = i2c_transfer(state->i2c, &msg, 1);
199f11ec7d4SSteven Toth 	if (err != 1) {
2004bd69e7bSMauro Carvalho Chehab 		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
2014bd69e7bSMauro Carvalho Chehab 		       __func__, err, reg, data);
2020d46748cSSteven Toth 		return -EREMOTEIO;
2030d46748cSSteven Toth 	}
2040d46748cSSteven Toth 
2050d46748cSSteven Toth 	return 0;
2060d46748cSSteven Toth }
2070d46748cSSteven Toth 
2080d46748cSSteven Toth /* Bulk byte writes to a single I2C address, for 32k firmware load */
209f11ec7d4SSteven Toth static int cx24116_writeregN(struct cx24116_state *state, int reg,
21064decbfeSGeert Uytterhoeven 			     const u8 *data, u16 len)
2110d46748cSSteven Toth {
212d303b7c5SMarkus Elfring 	int ret;
2130d46748cSSteven Toth 	struct i2c_msg msg;
2140d46748cSSteven Toth 	u8 *buf;
2150d46748cSSteven Toth 
2160d46748cSSteven Toth 	buf = kmalloc(len + 1, GFP_KERNEL);
2179722e569SMarkus Elfring 	if (!buf)
2189722e569SMarkus Elfring 		return -ENOMEM;
2190d46748cSSteven Toth 
2200d46748cSSteven Toth 	*(buf) = reg;
2210d46748cSSteven Toth 	memcpy(buf + 1, data, len);
2220d46748cSSteven Toth 
2230d46748cSSteven Toth 	msg.addr = state->config->demod_address;
2240d46748cSSteven Toth 	msg.flags = 0;
2250d46748cSSteven Toth 	msg.buf = buf;
2260d46748cSSteven Toth 	msg.len = len + 1;
2270d46748cSSteven Toth 
2280d46748cSSteven Toth 	if (debug > 1)
22998c94823SSteven Toth 		printk(KERN_INFO "cx24116: %s:  write regN 0x%02x, len = %d\n",
2300d46748cSSteven Toth 			__func__, reg, len);
2310d46748cSSteven Toth 
232f11ec7d4SSteven Toth 	ret = i2c_transfer(state->i2c, &msg, 1);
233f11ec7d4SSteven Toth 	if (ret != 1) {
23498c94823SSteven Toth 		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
2350d46748cSSteven Toth 			 __func__, ret, reg);
2360d46748cSSteven Toth 		ret = -EREMOTEIO;
2370d46748cSSteven Toth 	}
2380d46748cSSteven Toth 
2390d46748cSSteven Toth 	kfree(buf);
2400d46748cSSteven Toth 
2410d46748cSSteven Toth 	return ret;
2420d46748cSSteven Toth }
2430d46748cSSteven Toth 
2440d46748cSSteven Toth static int cx24116_readreg(struct cx24116_state *state, u8 reg)
2450d46748cSSteven Toth {
2460d46748cSSteven Toth 	int ret;
2470d46748cSSteven Toth 	u8 b0[] = { reg };
2480d46748cSSteven Toth 	u8 b1[] = { 0 };
2490d46748cSSteven Toth 	struct i2c_msg msg[] = {
250f11ec7d4SSteven Toth 		{ .addr = state->config->demod_address, .flags = 0,
251f11ec7d4SSteven Toth 			.buf = b0, .len = 1 },
252f11ec7d4SSteven Toth 		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
253f11ec7d4SSteven Toth 			.buf = b1, .len = 1 }
2540d46748cSSteven Toth 	};
2550d46748cSSteven Toth 
2560d46748cSSteven Toth 	ret = i2c_transfer(state->i2c, msg, 2);
2570d46748cSSteven Toth 
2580d46748cSSteven Toth 	if (ret != 2) {
25998c94823SSteven Toth 		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
26098c94823SSteven Toth 			__func__, reg, ret);
2610d46748cSSteven Toth 		return ret;
2620d46748cSSteven Toth 	}
2630d46748cSSteven Toth 
2640d46748cSSteven Toth 	if (debug > 1)
26598c94823SSteven Toth 		printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n",
266f11ec7d4SSteven Toth 			reg, b1[0]);
2670d46748cSSteven Toth 
2680d46748cSSteven Toth 	return b1[0];
2690d46748cSSteven Toth }
2700d46748cSSteven Toth 
27198c94823SSteven Toth static int cx24116_set_inversion(struct cx24116_state *state,
2720df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion)
2730d46748cSSteven Toth {
2740d46748cSSteven Toth 	dprintk("%s(%d)\n", __func__, inversion);
2750d46748cSSteven Toth 
2760d46748cSSteven Toth 	switch (inversion) {
2770d46748cSSteven Toth 	case INVERSION_OFF:
2780d46748cSSteven Toth 		state->dnxt.inversion_val = 0x00;
2790d46748cSSteven Toth 		break;
2800d46748cSSteven Toth 	case INVERSION_ON:
2810d46748cSSteven Toth 		state->dnxt.inversion_val = 0x04;
2820d46748cSSteven Toth 		break;
2830d46748cSSteven Toth 	case INVERSION_AUTO:
2840d46748cSSteven Toth 		state->dnxt.inversion_val = 0x0C;
2850d46748cSSteven Toth 		break;
2860d46748cSSteven Toth 	default:
2870d46748cSSteven Toth 		return -EINVAL;
2880d46748cSSteven Toth 	}
2890d46748cSSteven Toth 
2900d46748cSSteven Toth 	state->dnxt.inversion = inversion;
2910d46748cSSteven Toth 
2920d46748cSSteven Toth 	return 0;
2930d46748cSSteven Toth }
2940d46748cSSteven Toth 
295490c8684SDarron Broad /*
296490c8684SDarron Broad  * modfec (modulation and FEC)
297490c8684SDarron Broad  * ===========================
298490c8684SDarron Broad  *
299490c8684SDarron Broad  * MOD          FEC             mask/val    standard
300490c8684SDarron Broad  * ----         --------        ----------- --------
301490c8684SDarron Broad  * QPSK         FEC_1_2         0x02 0x02+X DVB-S
302490c8684SDarron Broad  * QPSK         FEC_2_3         0x04 0x02+X DVB-S
303490c8684SDarron Broad  * QPSK         FEC_3_4         0x08 0x02+X DVB-S
304490c8684SDarron Broad  * QPSK         FEC_4_5         0x10 0x02+X DVB-S (?)
305490c8684SDarron Broad  * QPSK         FEC_5_6         0x20 0x02+X DVB-S
306490c8684SDarron Broad  * QPSK         FEC_6_7         0x40 0x02+X DVB-S
307490c8684SDarron Broad  * QPSK         FEC_7_8         0x80 0x02+X DVB-S
308490c8684SDarron Broad  * QPSK         FEC_8_9         0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
309490c8684SDarron Broad  * QPSK         AUTO            0xff 0x02+X DVB-S
310490c8684SDarron Broad  *
311490c8684SDarron Broad  * For DVB-S high byte probably represents FEC
312490c8684SDarron Broad  * and low byte selects the modulator. The high
313490c8684SDarron Broad  * byte is search range mask. Bit 5 may turn
314490c8684SDarron Broad  * on DVB-S and remaining bits represent some
315490c8684SDarron Broad  * kind of calibration (how/what i do not know).
316490c8684SDarron Broad  *
317490c8684SDarron Broad  * Eg.(2/3) szap "Zone Horror"
318490c8684SDarron Broad  *
319490c8684SDarron Broad  * mask/val = 0x04, 0x20
32098c94823SSteven Toth  * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK
321490c8684SDarron Broad  *
322490c8684SDarron Broad  * mask/val = 0x04, 0x30
32398c94823SSteven Toth  * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK
324490c8684SDarron Broad  *
325490c8684SDarron Broad  * After tuning FECSTATUS contains actual FEC
326490c8684SDarron Broad  * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
327490c8684SDarron Broad  *
328490c8684SDarron Broad  * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
329490c8684SDarron Broad  *
330490c8684SDarron Broad  * NBC-QPSK     FEC_1_2         0x00, 0x04      DVB-S2
331490c8684SDarron Broad  * NBC-QPSK     FEC_3_5         0x00, 0x05      DVB-S2
332490c8684SDarron Broad  * NBC-QPSK     FEC_2_3         0x00, 0x06      DVB-S2
333490c8684SDarron Broad  * NBC-QPSK     FEC_3_4         0x00, 0x07      DVB-S2
334490c8684SDarron Broad  * NBC-QPSK     FEC_4_5         0x00, 0x08      DVB-S2
335490c8684SDarron Broad  * NBC-QPSK     FEC_5_6         0x00, 0x09      DVB-S2
336490c8684SDarron Broad  * NBC-QPSK     FEC_8_9         0x00, 0x0a      DVB-S2
337490c8684SDarron Broad  * NBC-QPSK     FEC_9_10        0x00, 0x0b      DVB-S2
338490c8684SDarron Broad  *
339490c8684SDarron Broad  * NBC-8PSK     FEC_3_5         0x00, 0x0c      DVB-S2
340490c8684SDarron Broad  * NBC-8PSK     FEC_2_3         0x00, 0x0d      DVB-S2
341490c8684SDarron Broad  * NBC-8PSK     FEC_3_4         0x00, 0x0e      DVB-S2
342490c8684SDarron Broad  * NBC-8PSK     FEC_5_6         0x00, 0x0f      DVB-S2
343490c8684SDarron Broad  * NBC-8PSK     FEC_8_9         0x00, 0x10      DVB-S2
344490c8684SDarron Broad  * NBC-8PSK     FEC_9_10        0x00, 0x11      DVB-S2
345490c8684SDarron Broad  *
346490c8684SDarron Broad  * For DVB-S2 low bytes selects both modulator
347490c8684SDarron Broad  * and FEC. High byte is meaningless here. To
348490c8684SDarron Broad  * set pilot, bit 6 (0x40) is set. When inspecting
349490c8684SDarron Broad  * FECSTATUS bit 7 (0x80) represents the pilot
350490c8684SDarron Broad  * selection whilst not tuned. When tuned, actual FEC
351490c8684SDarron Broad  * in use is found in FECSTATUS as per above. Pilot
352490c8684SDarron Broad  * value is reset.
353490c8684SDarron Broad  */
354490c8684SDarron Broad 
3550d46748cSSteven Toth /* A table of modulation, fec and configuration bytes for the demod.
3560d46748cSSteven Toth  * Not all S2 mmodulation schemes are support and not all rates with
3570d46748cSSteven Toth  * a scheme are support. Especially, no auto detect when in S2 mode.
3580d46748cSSteven Toth  */
359ffbc5f88SMauro Carvalho Chehab static struct cx24116_modfec {
3600df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delivery_system;
3610df289a2SMauro Carvalho Chehab 	enum fe_modulation modulation;
3620df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
3630d46748cSSteven Toth 	u8 mask;	/* In DVBS mode this is used to autodetect */
3640d46748cSSteven Toth 	u8 val;		/* Passed to the firmware to indicate mode selection */
3650d46748cSSteven Toth } CX24116_MODFEC_MODES[] = {
3660d46748cSSteven Toth  /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
367490c8684SDarron Broad 
368490c8684SDarron Broad  /*mod   fec       mask  val */
3690a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
3700a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
3710a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
3720a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
3730a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
3740a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
3750a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
3760a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
3770a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
3780a6393aeSSteven Toth  { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
3790d46748cSSteven Toth  /* NBC-QPSK */
3800a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
3810a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
3820a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
3830a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
3840a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
3850a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
3860a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
3870a6393aeSSteven Toth  { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
3880d46748cSSteven Toth  /* 8PSK */
3890a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
3900a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
3910a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
3920a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
3930a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
3940a6393aeSSteven Toth  { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
395490c8684SDarron Broad  /*
396490c8684SDarron Broad   * `val' can be found in the FECSTATUS register when tuning.
397490c8684SDarron Broad   * FECSTATUS will give the actual FEC in use if tuning was successful.
398490c8684SDarron Broad   */
3990d46748cSSteven Toth };
4000d46748cSSteven Toth 
4010d46748cSSteven Toth static int cx24116_lookup_fecmod(struct cx24116_state *state,
4020df289a2SMauro Carvalho Chehab 	enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
4030d46748cSSteven Toth {
4040d46748cSSteven Toth 	int i, ret = -EOPNOTSUPP;
4050d46748cSSteven Toth 
406490c8684SDarron Broad 	dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
407490c8684SDarron Broad 
408f11ec7d4SSteven Toth 	for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) {
4093569476dSDarron Broad 		if ((d == CX24116_MODFEC_MODES[i].delivery_system) &&
4103569476dSDarron Broad 			(m == CX24116_MODFEC_MODES[i].modulation) &&
411f11ec7d4SSteven Toth 			(f == CX24116_MODFEC_MODES[i].fec)) {
4120d46748cSSteven Toth 				ret = i;
4130d46748cSSteven Toth 				break;
4140d46748cSSteven Toth 			}
4150d46748cSSteven Toth 	}
4160d46748cSSteven Toth 
4170d46748cSSteven Toth 	return ret;
4180d46748cSSteven Toth }
4190d46748cSSteven Toth 
42098c94823SSteven Toth static int cx24116_set_fec(struct cx24116_state *state,
4210df289a2SMauro Carvalho Chehab 			   enum fe_delivery_system delsys,
4220df289a2SMauro Carvalho Chehab 			   enum fe_modulation mod,
4230df289a2SMauro Carvalho Chehab 			   enum fe_code_rate fec)
4240d46748cSSteven Toth {
4250d46748cSSteven Toth 	int ret = 0;
426490c8684SDarron Broad 
427490c8684SDarron Broad 	dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
4280d46748cSSteven Toth 
4293569476dSDarron Broad 	ret = cx24116_lookup_fecmod(state, delsys, mod, fec);
4300d46748cSSteven Toth 
4310d46748cSSteven Toth 	if (ret < 0)
4320d46748cSSteven Toth 		return ret;
4330d46748cSSteven Toth 
434490c8684SDarron Broad 	state->dnxt.fec = fec;
4350d46748cSSteven Toth 	state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
4360d46748cSSteven Toth 	state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
437490c8684SDarron Broad 	dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
438490c8684SDarron Broad 		state->dnxt.fec_mask, state->dnxt.fec_val);
4390d46748cSSteven Toth 
4400d46748cSSteven Toth 	return 0;
4410d46748cSSteven Toth }
4420d46748cSSteven Toth 
4430d46748cSSteven Toth static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate)
4440d46748cSSteven Toth {
445490c8684SDarron Broad 	dprintk("%s(%d)\n", __func__, rate);
4460d46748cSSteven Toth 
4470d46748cSSteven Toth 	/*  check if symbol rate is within limits */
448490c8684SDarron Broad 	if ((rate > state->frontend.ops.info.symbol_rate_max) ||
449490c8684SDarron Broad 	    (rate < state->frontend.ops.info.symbol_rate_min)) {
450490c8684SDarron Broad 		dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
451490c8684SDarron Broad 		return -EOPNOTSUPP;
452490c8684SDarron Broad 	}
4530d46748cSSteven Toth 
454490c8684SDarron Broad 	state->dnxt.symbol_rate = rate;
455490c8684SDarron Broad 	dprintk("%s() symbol_rate = %d\n", __func__, rate);
456490c8684SDarron Broad 
457490c8684SDarron Broad 	return 0;
4580d46748cSSteven Toth }
4590d46748cSSteven Toth 
460f11ec7d4SSteven Toth static int cx24116_load_firmware(struct dvb_frontend *fe,
461f11ec7d4SSteven Toth 	const struct firmware *fw);
4620d46748cSSteven Toth 
4630d46748cSSteven Toth static int cx24116_firmware_ondemand(struct dvb_frontend *fe)
4640d46748cSSteven Toth {
4650d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
4660d46748cSSteven Toth 	const struct firmware *fw;
4670d46748cSSteven Toth 	int ret = 0;
4680d46748cSSteven Toth 
4690d46748cSSteven Toth 	dprintk("%s()\n", __func__);
4700d46748cSSteven Toth 
471f11ec7d4SSteven Toth 	if (cx24116_readreg(state, 0x20) > 0) {
4720d46748cSSteven Toth 
4730d46748cSSteven Toth 		if (state->skip_fw_load)
4740d46748cSSteven Toth 			return 0;
4750d46748cSSteven Toth 
4760d46748cSSteven Toth 		/* Load firmware */
47798c94823SSteven Toth 		/* request the firmware, this will block until loaded */
47898c94823SSteven Toth 		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n",
47998c94823SSteven Toth 			__func__, CX24116_DEFAULT_FIRMWARE);
48098c94823SSteven Toth 		ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE,
481e9785250SJean Delvare 			state->i2c->dev.parent);
48298c94823SSteven Toth 		printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n",
48398c94823SSteven Toth 			__func__);
4840d46748cSSteven Toth 		if (ret) {
4854bd69e7bSMauro Carvalho Chehab 			printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
4864bd69e7bSMauro Carvalho Chehab 			       __func__);
4870d46748cSSteven Toth 			return ret;
4880d46748cSSteven Toth 		}
4890d46748cSSteven Toth 
49098c94823SSteven Toth 		/* Make sure we don't recurse back through here
49198c94823SSteven Toth 		 * during loading */
4920d46748cSSteven Toth 		state->skip_fw_load = 1;
4930d46748cSSteven Toth 
4940d46748cSSteven Toth 		ret = cx24116_load_firmware(fe, fw);
4950d46748cSSteven Toth 		if (ret)
49698c94823SSteven Toth 			printk(KERN_ERR "%s: Writing firmware to device failed\n",
49798c94823SSteven Toth 				__func__);
4980d46748cSSteven Toth 
4990d46748cSSteven Toth 		release_firmware(fw);
5000d46748cSSteven Toth 
50198c94823SSteven Toth 		printk(KERN_INFO "%s: Firmware upload %s\n", __func__,
50298c94823SSteven Toth 			ret == 0 ? "complete" : "failed");
5030d46748cSSteven Toth 
5040d46748cSSteven Toth 		/* Ensure firmware is always loaded if required */
5050d46748cSSteven Toth 		state->skip_fw_load = 0;
5060d46748cSSteven Toth 	}
5070d46748cSSteven Toth 
5080d46748cSSteven Toth 	return ret;
5090d46748cSSteven Toth }
5100d46748cSSteven Toth 
51198c94823SSteven Toth /* Take a basic firmware command structure, format it
51298c94823SSteven Toth  * and forward it for processing
51398c94823SSteven Toth  */
5140d46748cSSteven Toth static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd)
5150d46748cSSteven Toth {
5160d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
5170d46748cSSteven Toth 	int i, ret;
5180d46748cSSteven Toth 
5190d46748cSSteven Toth 	dprintk("%s()\n", __func__);
5200d46748cSSteven Toth 
5210d46748cSSteven Toth 	/* Load the firmware if required */
522f11ec7d4SSteven Toth 	ret = cx24116_firmware_ondemand(fe);
523f11ec7d4SSteven Toth 	if (ret != 0) {
52498c94823SSteven Toth 		printk(KERN_ERR "%s(): Unable initialise the firmware\n",
52598c94823SSteven Toth 			__func__);
5260d46748cSSteven Toth 		return ret;
5270d46748cSSteven Toth 	}
5280d46748cSSteven Toth 
5290d46748cSSteven Toth 	/* Write the command */
530f11ec7d4SSteven Toth 	for (i = 0; i < cmd->len ; i++) {
5310d46748cSSteven Toth 		dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
5320d46748cSSteven Toth 		cx24116_writereg(state, i, cmd->args[i]);
5330d46748cSSteven Toth 	}
5340d46748cSSteven Toth 
5350d46748cSSteven Toth 	/* Start execution and wait for cmd to terminate */
536490c8684SDarron Broad 	cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
537f11ec7d4SSteven Toth 	while (cx24116_readreg(state, CX24116_REG_EXECUTE)) {
5380d46748cSSteven Toth 		msleep(10);
539f11ec7d4SSteven Toth 		if (i++ > 64) {
54098c94823SSteven Toth 			/* Avoid looping forever if the firmware does
54198c94823SSteven Toth 				not respond */
54298c94823SSteven Toth 			printk(KERN_WARNING "%s() Firmware not responding\n",
54398c94823SSteven Toth 				__func__);
5440d46748cSSteven Toth 			return -EREMOTEIO;
5450d46748cSSteven Toth 		}
5460d46748cSSteven Toth 	}
5470d46748cSSteven Toth 	return 0;
5480d46748cSSteven Toth }
5490d46748cSSteven Toth 
550f11ec7d4SSteven Toth static int cx24116_load_firmware(struct dvb_frontend *fe,
551f11ec7d4SSteven Toth 	const struct firmware *fw)
5520d46748cSSteven Toth {
5530d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
5540d46748cSSteven Toth 	struct cx24116_cmd cmd;
555e0bae9b3SAntti Palosaari 	int i, ret, len, max, remaining;
556490c8684SDarron Broad 	unsigned char vers[4];
5570d46748cSSteven Toth 
5580d46748cSSteven Toth 	dprintk("%s\n", __func__);
559f11ec7d4SSteven Toth 	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
560f11ec7d4SSteven Toth 			fw->size,
561f11ec7d4SSteven Toth 			fw->data[0],
562f11ec7d4SSteven Toth 			fw->data[1],
563f11ec7d4SSteven Toth 			fw->data[fw->size-2],
564f11ec7d4SSteven Toth 			fw->data[fw->size-1]);
5650d46748cSSteven Toth 
5660d46748cSSteven Toth 	/* Toggle 88x SRST pin to reset demod */
5670d46748cSSteven Toth 	if (state->config->reset_device)
5680d46748cSSteven Toth 		state->config->reset_device(fe);
5690d46748cSSteven Toth 
5700d46748cSSteven Toth 	/* Begin the firmware load process */
5710d46748cSSteven Toth 	/* Prepare the demod, load the firmware, cleanup after load */
5720d46748cSSteven Toth 
573490c8684SDarron Broad 	/* Init PLL */
574490c8684SDarron Broad 	cx24116_writereg(state, 0xE5, 0x00);
575490c8684SDarron Broad 	cx24116_writereg(state, 0xF1, 0x08);
576490c8684SDarron Broad 	cx24116_writereg(state, 0xF2, 0x13);
577490c8684SDarron Broad 
578490c8684SDarron Broad 	/* Start PLL */
579490c8684SDarron Broad 	cx24116_writereg(state, 0xe0, 0x03);
580490c8684SDarron Broad 	cx24116_writereg(state, 0xe0, 0x00);
581490c8684SDarron Broad 
582490c8684SDarron Broad 	/* Unknown */
583490c8684SDarron Broad 	cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
584490c8684SDarron Broad 	cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
585490c8684SDarron Broad 
586490c8684SDarron Broad 	/* Unknown */
5870d46748cSSteven Toth 	cx24116_writereg(state, 0xF0, 0x03);
5880d46748cSSteven Toth 	cx24116_writereg(state, 0xF4, 0x81);
5890d46748cSSteven Toth 	cx24116_writereg(state, 0xF5, 0x00);
5900d46748cSSteven Toth 	cx24116_writereg(state, 0xF6, 0x00);
5910d46748cSSteven Toth 
592107d7b18SAntti Palosaari 	/* Split firmware to the max I2C write len and write.
593e0bae9b3SAntti Palosaari 	 * Writes whole firmware as one write when i2c_wr_max is set to 0. */
594e0bae9b3SAntti Palosaari 	if (state->config->i2c_wr_max)
595e0bae9b3SAntti Palosaari 		max = state->config->i2c_wr_max;
596e0bae9b3SAntti Palosaari 	else
597e0bae9b3SAntti Palosaari 		max = INT_MAX; /* enough for 32k firmware */
598107d7b18SAntti Palosaari 
599e0bae9b3SAntti Palosaari 	for (remaining = fw->size; remaining > 0; remaining -= max - 1) {
600107d7b18SAntti Palosaari 		len = remaining;
601e0bae9b3SAntti Palosaari 		if (len > max - 1)
602e0bae9b3SAntti Palosaari 			len = max - 1;
603107d7b18SAntti Palosaari 
604107d7b18SAntti Palosaari 		cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining],
605107d7b18SAntti Palosaari 			len);
606107d7b18SAntti Palosaari 	}
6070d46748cSSteven Toth 
6080d46748cSSteven Toth 	cx24116_writereg(state, 0xF4, 0x10);
6090d46748cSSteven Toth 	cx24116_writereg(state, 0xF0, 0x00);
6100d46748cSSteven Toth 	cx24116_writereg(state, 0xF8, 0x06);
6110d46748cSSteven Toth 
612490c8684SDarron Broad 	/* Firmware CMD 10: VCO config */
613490c8684SDarron Broad 	cmd.args[0x00] = CMD_SET_VCO;
6140d46748cSSteven Toth 	cmd.args[0x01] = 0x05;
6150d46748cSSteven Toth 	cmd.args[0x02] = 0xdc;
6160d46748cSSteven Toth 	cmd.args[0x03] = 0xda;
6170d46748cSSteven Toth 	cmd.args[0x04] = 0xae;
6180d46748cSSteven Toth 	cmd.args[0x05] = 0xaa;
6190d46748cSSteven Toth 	cmd.args[0x06] = 0x04;
6200d46748cSSteven Toth 	cmd.args[0x07] = 0x9d;
6210d46748cSSteven Toth 	cmd.args[0x08] = 0xfc;
6220d46748cSSteven Toth 	cmd.args[0x09] = 0x06;
6230d46748cSSteven Toth 	cmd.len = 0x0a;
6240d46748cSSteven Toth 	ret = cx24116_cmd_execute(fe, &cmd);
6250d46748cSSteven Toth 	if (ret != 0)
6260d46748cSSteven Toth 		return ret;
6270d46748cSSteven Toth 
628490c8684SDarron Broad 	cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
6290d46748cSSteven Toth 
630490c8684SDarron Broad 	/* Firmware CMD 14: Tuner config */
631490c8684SDarron Broad 	cmd.args[0x00] = CMD_TUNERINIT;
6320d46748cSSteven Toth 	cmd.args[0x01] = 0x00;
6330d46748cSSteven Toth 	cmd.args[0x02] = 0x00;
6340d46748cSSteven Toth 	cmd.len = 0x03;
6350d46748cSSteven Toth 	ret = cx24116_cmd_execute(fe, &cmd);
6360d46748cSSteven Toth 	if (ret != 0)
6370d46748cSSteven Toth 		return ret;
6380d46748cSSteven Toth 
6390d46748cSSteven Toth 	cx24116_writereg(state, 0xe5, 0x00);
6400d46748cSSteven Toth 
641490c8684SDarron Broad 	/* Firmware CMD 13: MPEG config */
642490c8684SDarron Broad 	cmd.args[0x00] = CMD_MPEGCONFIG;
6430d46748cSSteven Toth 	cmd.args[0x01] = 0x01;
6440d46748cSSteven Toth 	cmd.args[0x02] = 0x75;
6450d46748cSSteven Toth 	cmd.args[0x03] = 0x00;
646cc8c4f3aSIgor M. Liplianin 	if (state->config->mpg_clk_pos_pol)
647cc8c4f3aSIgor M. Liplianin 		cmd.args[0x04] = state->config->mpg_clk_pos_pol;
648cc8c4f3aSIgor M. Liplianin 	else
6490d46748cSSteven Toth 		cmd.args[0x04] = 0x02;
6500d46748cSSteven Toth 	cmd.args[0x05] = 0x00;
6510d46748cSSteven Toth 	cmd.len = 0x06;
6520d46748cSSteven Toth 	ret = cx24116_cmd_execute(fe, &cmd);
6530d46748cSSteven Toth 	if (ret != 0)
6540d46748cSSteven Toth 		return ret;
6550d46748cSSteven Toth 
656490c8684SDarron Broad 	/* Firmware CMD 35: Get firmware version */
657490c8684SDarron Broad 	cmd.args[0x00] = CMD_UPDFWVERS;
658490c8684SDarron Broad 	cmd.len = 0x02;
659490c8684SDarron Broad 	for (i = 0; i < 4; i++) {
660490c8684SDarron Broad 		cmd.args[0x01] = i;
661490c8684SDarron Broad 		ret = cx24116_cmd_execute(fe, &cmd);
662490c8684SDarron Broad 		if (ret != 0)
663490c8684SDarron Broad 			return ret;
664490c8684SDarron Broad 		vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX);
665490c8684SDarron Broad 	}
66698c94823SSteven Toth 	printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__,
667490c8684SDarron Broad 		vers[0], vers[1], vers[2], vers[3]);
668490c8684SDarron Broad 
6690d46748cSSteven Toth 	return 0;
6700d46748cSSteven Toth }
6710d46748cSSteven Toth 
6720df289a2SMauro Carvalho Chehab static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status)
6730d46748cSSteven Toth {
6740d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
6750d46748cSSteven Toth 
6766639f1e0SDarron Broad 	int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) &
6776639f1e0SDarron Broad 		CX24116_STATUS_MASK;
6780d46748cSSteven Toth 
6790d46748cSSteven Toth 	dprintk("%s: status = 0x%02x\n", __func__, lock);
6800d46748cSSteven Toth 
6810d46748cSSteven Toth 	*status = 0;
6820d46748cSSteven Toth 
683490c8684SDarron Broad 	if (lock & CX24116_HAS_SIGNAL)
6840d46748cSSteven Toth 		*status |= FE_HAS_SIGNAL;
685490c8684SDarron Broad 	if (lock & CX24116_HAS_CARRIER)
6860d46748cSSteven Toth 		*status |= FE_HAS_CARRIER;
687490c8684SDarron Broad 	if (lock & CX24116_HAS_VITERBI)
6880d46748cSSteven Toth 		*status |= FE_HAS_VITERBI;
689490c8684SDarron Broad 	if (lock & CX24116_HAS_SYNCLOCK)
6900d46748cSSteven Toth 		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
6910d46748cSSteven Toth 
6920d46748cSSteven Toth 	return 0;
6930d46748cSSteven Toth }
6940d46748cSSteven Toth 
6950d46748cSSteven Toth static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber)
6960d46748cSSteven Toth {
697490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
698490c8684SDarron Broad 
6990d46748cSSteven Toth 	dprintk("%s()\n", __func__);
700490c8684SDarron Broad 
701490c8684SDarron Broad 	*ber =  (cx24116_readreg(state, CX24116_REG_BER24) << 24) |
702490c8684SDarron Broad 		(cx24116_readreg(state, CX24116_REG_BER16) << 16) |
703490c8684SDarron Broad 		(cx24116_readreg(state, CX24116_REG_BER8)  << 8)  |
704490c8684SDarron Broad 		 cx24116_readreg(state, CX24116_REG_BER0);
7050d46748cSSteven Toth 
7060d46748cSSteven Toth 	return 0;
7070d46748cSSteven Toth }
7080d46748cSSteven Toth 
709490c8684SDarron Broad /* TODO Determine function and scale appropriately */
710f11ec7d4SSteven Toth static int cx24116_read_signal_strength(struct dvb_frontend *fe,
711f11ec7d4SSteven Toth 	u16 *signal_strength)
7120d46748cSSteven Toth {
7130d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
714490c8684SDarron Broad 	struct cx24116_cmd cmd;
715490c8684SDarron Broad 	int ret;
716490c8684SDarron Broad 	u16 sig_reading;
717490c8684SDarron Broad 
718490c8684SDarron Broad 	dprintk("%s()\n", __func__);
719490c8684SDarron Broad 
720490c8684SDarron Broad 	/* Firmware CMD 19: Get AGC */
721490c8684SDarron Broad 	cmd.args[0x00] = CMD_GETAGC;
722490c8684SDarron Broad 	cmd.len = 0x01;
723490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &cmd);
724490c8684SDarron Broad 	if (ret != 0)
725490c8684SDarron Broad 		return ret;
726490c8684SDarron Broad 
727f11ec7d4SSteven Toth 	sig_reading =
728f11ec7d4SSteven Toth 		(cx24116_readreg(state,
729f11ec7d4SSteven Toth 			CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) |
730490c8684SDarron Broad 		(cx24116_readreg(state, CX24116_REG_SIGNAL) << 6);
731490c8684SDarron Broad 	*signal_strength = 0 - sig_reading;
732490c8684SDarron Broad 
733f11ec7d4SSteven Toth 	dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n",
734f11ec7d4SSteven Toth 		__func__, sig_reading, *signal_strength);
735490c8684SDarron Broad 
736490c8684SDarron Broad 	return 0;
737490c8684SDarron Broad }
738490c8684SDarron Broad 
739490c8684SDarron Broad /* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
7408953db79SSteven Toth static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr)
741490c8684SDarron Broad {
742490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
743490c8684SDarron Broad 	u8 snr_reading;
744490c8684SDarron Broad 	static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
745490c8684SDarron Broad 		0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667,
746490c8684SDarron Broad 		0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667,
747f11ec7d4SSteven Toth 		0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667,
748f11ec7d4SSteven Toth 		0x18000 };
7490d46748cSSteven Toth 
7500d46748cSSteven Toth 	dprintk("%s()\n", __func__);
7510d46748cSSteven Toth 
7528953db79SSteven Toth 	snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
7530d46748cSSteven Toth 
754490c8684SDarron Broad 	if (snr_reading >= 0xa0 /* 100% */)
755490c8684SDarron Broad 		*snr = 0xffff;
7560d46748cSSteven Toth 	else
757490c8684SDarron Broad 		*snr = snr_tab[(snr_reading & 0xf0) >> 4] +
758490c8684SDarron Broad 			(snr_tab[(snr_reading & 0x0f)] >> 4);
7590d46748cSSteven Toth 
760490c8684SDarron Broad 	dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
761490c8684SDarron Broad 		snr_reading, *snr);
7620d46748cSSteven Toth 
7630d46748cSSteven Toth 	return 0;
7640d46748cSSteven Toth }
7650d46748cSSteven Toth 
7668953db79SSteven Toth /* The reelbox patches show the value in the registers represents
7678953db79SSteven Toth  * ESNO, from 0->30db (values 0->300). We provide this value by
7688953db79SSteven Toth  * default.
7698953db79SSteven Toth  */
7708953db79SSteven Toth static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr)
7718953db79SSteven Toth {
7728953db79SSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
7738953db79SSteven Toth 
7748953db79SSteven Toth 	dprintk("%s()\n", __func__);
7758953db79SSteven Toth 
7768953db79SSteven Toth 	*snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 |
7778953db79SSteven Toth 		cx24116_readreg(state, CX24116_REG_QUALITY0);
7788953db79SSteven Toth 
7798953db79SSteven Toth 	dprintk("%s: raw 0x%04x\n", __func__, *snr);
7808953db79SSteven Toth 
7818953db79SSteven Toth 	return 0;
7828953db79SSteven Toth }
7838953db79SSteven Toth 
7848953db79SSteven Toth static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr)
7858953db79SSteven Toth {
7868953db79SSteven Toth 	if (esno_snr == 1)
7878953db79SSteven Toth 		return cx24116_read_snr_esno(fe, snr);
7888953db79SSteven Toth 	else
7898953db79SSteven Toth 		return cx24116_read_snr_pct(fe, snr);
7908953db79SSteven Toth }
7918953db79SSteven Toth 
7920d46748cSSteven Toth static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
7930d46748cSSteven Toth {
794490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
795490c8684SDarron Broad 
7960d46748cSSteven Toth 	dprintk("%s()\n", __func__);
797490c8684SDarron Broad 
798490c8684SDarron Broad 	*ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) |
799490c8684SDarron Broad 		cx24116_readreg(state, CX24116_REG_UCB0);
8000d46748cSSteven Toth 
8010d46748cSSteven Toth 	return 0;
8020d46748cSSteven Toth }
8030d46748cSSteven Toth 
8040d46748cSSteven Toth /* Overwrite the current tuning params, we are about to tune */
8050d46748cSSteven Toth static void cx24116_clone_params(struct dvb_frontend *fe)
8060d46748cSSteven Toth {
8070d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
808ee45ddc1SEzequiel Garcia 	state->dcur = state->dnxt;
8090d46748cSSteven Toth }
8100d46748cSSteven Toth 
811490c8684SDarron Broad /* Wait for LNB */
812490c8684SDarron Broad static int cx24116_wait_for_lnb(struct dvb_frontend *fe)
813490c8684SDarron Broad {
814490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
815490c8684SDarron Broad 	int i;
816490c8684SDarron Broad 
817490c8684SDarron Broad 	dprintk("%s() qstatus = 0x%02x\n", __func__,
818490c8684SDarron Broad 		cx24116_readreg(state, CX24116_REG_QSTATUS));
819490c8684SDarron Broad 
820490c8684SDarron Broad 	/* Wait for up to 300 ms */
821490c8684SDarron Broad 	for (i = 0; i < 30 ; i++) {
822490c8684SDarron Broad 		if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
823490c8684SDarron Broad 			return 0;
824490c8684SDarron Broad 		msleep(10);
825490c8684SDarron Broad 	}
826490c8684SDarron Broad 
827490c8684SDarron Broad 	dprintk("%s(): LNB not ready\n", __func__);
828490c8684SDarron Broad 
829490c8684SDarron Broad 	return -ETIMEDOUT; /* -EBUSY ? */
830490c8684SDarron Broad }
831490c8684SDarron Broad 
832c7bdcd0fSIgor M. Liplianin static int cx24116_set_voltage(struct dvb_frontend *fe,
8330df289a2SMauro Carvalho Chehab 	enum fe_sec_voltage voltage)
834c7bdcd0fSIgor M. Liplianin {
835c7bdcd0fSIgor M. Liplianin 	struct cx24116_cmd cmd;
836c7bdcd0fSIgor M. Liplianin 	int ret;
837c7bdcd0fSIgor M. Liplianin 
838c7bdcd0fSIgor M. Liplianin 	dprintk("%s: %s\n", __func__,
839c7bdcd0fSIgor M. Liplianin 		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
840c7bdcd0fSIgor M. Liplianin 		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
841c7bdcd0fSIgor M. Liplianin 
842c7bdcd0fSIgor M. Liplianin 	/* Wait for LNB ready */
843c7bdcd0fSIgor M. Liplianin 	ret = cx24116_wait_for_lnb(fe);
844c7bdcd0fSIgor M. Liplianin 	if (ret != 0)
845c7bdcd0fSIgor M. Liplianin 		return ret;
846c7bdcd0fSIgor M. Liplianin 
847c7bdcd0fSIgor M. Liplianin 	/* Wait for voltage/min repeat delay */
848c7bdcd0fSIgor M. Liplianin 	msleep(100);
849c7bdcd0fSIgor M. Liplianin 
850c7bdcd0fSIgor M. Liplianin 	cmd.args[0x00] = CMD_LNBDCLEVEL;
851c7bdcd0fSIgor M. Liplianin 	cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
852c7bdcd0fSIgor M. Liplianin 	cmd.len = 0x02;
853c7bdcd0fSIgor M. Liplianin 
854c7bdcd0fSIgor M. Liplianin 	/* Min delay time before DiSEqC send */
855c7bdcd0fSIgor M. Liplianin 	msleep(15);
856c7bdcd0fSIgor M. Liplianin 
857c7bdcd0fSIgor M. Liplianin 	return cx24116_cmd_execute(fe, &cmd);
858c7bdcd0fSIgor M. Liplianin }
859c7bdcd0fSIgor M. Liplianin 
860f11ec7d4SSteven Toth static int cx24116_set_tone(struct dvb_frontend *fe,
8610df289a2SMauro Carvalho Chehab 	enum fe_sec_tone_mode tone)
8620d46748cSSteven Toth {
8630d46748cSSteven Toth 	struct cx24116_cmd cmd;
8640d46748cSSteven Toth 	int ret;
8650d46748cSSteven Toth 
8660d46748cSSteven Toth 	dprintk("%s(%d)\n", __func__, tone);
8670d46748cSSteven Toth 	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
86898c94823SSteven Toth 		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
8690d46748cSSteven Toth 		return -EINVAL;
8700d46748cSSteven Toth 	}
8710d46748cSSteven Toth 
872490c8684SDarron Broad 	/* Wait for LNB ready */
873490c8684SDarron Broad 	ret = cx24116_wait_for_lnb(fe);
874490c8684SDarron Broad 	if (ret != 0)
875490c8684SDarron Broad 		return ret;
876490c8684SDarron Broad 
877490c8684SDarron Broad 	/* Min delay time after DiSEqC send */
878490c8684SDarron Broad 	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
879490c8684SDarron Broad 
8800d46748cSSteven Toth 	/* Now we set the tone */
8810d46748cSSteven Toth 	cmd.args[0x00] = CMD_SET_TONE;
8820d46748cSSteven Toth 	cmd.args[0x01] = 0x00;
8830d46748cSSteven Toth 	cmd.args[0x02] = 0x00;
8840d46748cSSteven Toth 
8850d46748cSSteven Toth 	switch (tone) {
8860d46748cSSteven Toth 	case SEC_TONE_ON:
8870d46748cSSteven Toth 		dprintk("%s: setting tone on\n", __func__);
8880d46748cSSteven Toth 		cmd.args[0x03] = 0x01;
8890d46748cSSteven Toth 		break;
8900d46748cSSteven Toth 	case SEC_TONE_OFF:
8910d46748cSSteven Toth 		dprintk("%s: setting tone off\n", __func__);
8920d46748cSSteven Toth 		cmd.args[0x03] = 0x00;
8930d46748cSSteven Toth 		break;
8940d46748cSSteven Toth 	}
8950d46748cSSteven Toth 	cmd.len = 0x04;
8960d46748cSSteven Toth 
897490c8684SDarron Broad 	/* Min delay time before DiSEqC send */
898490c8684SDarron Broad 	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
899490c8684SDarron Broad 
9000d46748cSSteven Toth 	return cx24116_cmd_execute(fe, &cmd);
9010d46748cSSteven Toth }
9020d46748cSSteven Toth 
9030d46748cSSteven Toth /* Initialise DiSEqC */
9040d46748cSSteven Toth static int cx24116_diseqc_init(struct dvb_frontend *fe)
9050d46748cSSteven Toth {
9060d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
907490c8684SDarron Broad 	struct cx24116_cmd cmd;
908490c8684SDarron Broad 	int ret;
9090d46748cSSteven Toth 
910490c8684SDarron Broad 	/* Firmware CMD 20: LNB/DiSEqC config */
911490c8684SDarron Broad 	cmd.args[0x00] = CMD_LNBCONFIG;
912490c8684SDarron Broad 	cmd.args[0x01] = 0x00;
913490c8684SDarron Broad 	cmd.args[0x02] = 0x10;
914490c8684SDarron Broad 	cmd.args[0x03] = 0x00;
915490c8684SDarron Broad 	cmd.args[0x04] = 0x8f;
916490c8684SDarron Broad 	cmd.args[0x05] = 0x28;
917490c8684SDarron Broad 	cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
918490c8684SDarron Broad 	cmd.args[0x07] = 0x01;
919490c8684SDarron Broad 	cmd.len = 0x08;
920490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &cmd);
921490c8684SDarron Broad 	if (ret != 0)
922490c8684SDarron Broad 		return ret;
923490c8684SDarron Broad 
924490c8684SDarron Broad 	/* Prepare a DiSEqC command */
925490c8684SDarron Broad 	state->dsec_cmd.args[0x00] = CMD_LNBSEND;
926490c8684SDarron Broad 
927490c8684SDarron Broad 	/* DiSEqC burst */
928490c8684SDarron Broad 	state->dsec_cmd.args[CX24116_DISEQC_BURST]  = CX24116_DISEQC_MINI_A;
929490c8684SDarron Broad 
930490c8684SDarron Broad 	/* Unknown */
931490c8684SDarron Broad 	state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
932490c8684SDarron Broad 	state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
93398c94823SSteven Toth 	/* Continuation flag? */
93498c94823SSteven Toth 	state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00;
935490c8684SDarron Broad 
936490c8684SDarron Broad 	/* DiSEqC message length */
937490c8684SDarron Broad 	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
938490c8684SDarron Broad 
939490c8684SDarron Broad 	/* Command length */
940490c8684SDarron Broad 	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS;
9410d46748cSSteven Toth 
9420d46748cSSteven Toth 	return 0;
9430d46748cSSteven Toth }
9440d46748cSSteven Toth 
9450d46748cSSteven Toth /* Send DiSEqC message with derived burst (hack) || previous burst */
946f11ec7d4SSteven Toth static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
947f11ec7d4SSteven Toth 	struct dvb_diseqc_master_cmd *d)
9480d46748cSSteven Toth {
9490d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
9500d46748cSSteven Toth 	int i, ret;
9510d46748cSSteven Toth 
9521fa2337aSMauro Carvalho Chehab 	/* Validate length */
9531fa2337aSMauro Carvalho Chehab 	if (d->msg_len > sizeof(d->msg))
9541fa2337aSMauro Carvalho Chehab 		return -EINVAL;
9551fa2337aSMauro Carvalho Chehab 
9560d46748cSSteven Toth 	/* Dump DiSEqC message */
9570d46748cSSteven Toth 	if (debug) {
95898c94823SSteven Toth 		printk(KERN_INFO "cx24116: %s(", __func__);
9590d46748cSSteven Toth 		for (i = 0 ; i < d->msg_len ;) {
96098c94823SSteven Toth 			printk(KERN_INFO "0x%02x", d->msg[i]);
9610d46748cSSteven Toth 			if (++i < d->msg_len)
96298c94823SSteven Toth 				printk(KERN_INFO ", ");
9630d46748cSSteven Toth 		}
964490c8684SDarron Broad 		printk(") toneburst=%d\n", toneburst);
9650d46748cSSteven Toth 	}
9660d46748cSSteven Toth 
9670d46748cSSteven Toth 	/* DiSEqC message */
9680d46748cSSteven Toth 	for (i = 0; i < d->msg_len; i++)
969490c8684SDarron Broad 		state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
9700d46748cSSteven Toth 
971490c8684SDarron Broad 	/* DiSEqC message length */
972490c8684SDarron Broad 	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
973490c8684SDarron Broad 
974490c8684SDarron Broad 	/* Command length */
975f11ec7d4SSteven Toth 	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS +
976f11ec7d4SSteven Toth 		state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
977490c8684SDarron Broad 
978490c8684SDarron Broad 	/* DiSEqC toneburst */
979490c8684SDarron Broad 	if (toneburst == CX24116_DISEQC_MESGCACHE)
980490c8684SDarron Broad 		/* Message is cached */
981490c8684SDarron Broad 		return 0;
982490c8684SDarron Broad 
983490c8684SDarron Broad 	else if (toneburst == CX24116_DISEQC_TONEOFF)
984490c8684SDarron Broad 		/* Message is sent without burst */
985490c8684SDarron Broad 		state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
986490c8684SDarron Broad 
987490c8684SDarron Broad 	else if (toneburst == CX24116_DISEQC_TONECACHE) {
988490c8684SDarron Broad 		/*
989490c8684SDarron Broad 		 * Message is sent with derived else cached burst
990490c8684SDarron Broad 		 *
991490c8684SDarron Broad 		 * WRITE PORT GROUP COMMAND 38
992490c8684SDarron Broad 		 *
993490c8684SDarron Broad 		 * 0/A/A: E0 10 38 F0..F3
994490c8684SDarron Broad 		 * 1/B/B: E0 10 38 F4..F7
995490c8684SDarron Broad 		 * 2/C/A: E0 10 38 F8..FB
996490c8684SDarron Broad 		 * 3/D/B: E0 10 38 FC..FF
997490c8684SDarron Broad 		 *
9987396d3eaSDarron Broad 		 * databyte[3]= 8421:8421
999490c8684SDarron Broad 		 *              ABCD:WXYZ
1000490c8684SDarron Broad 		 *              CLR :SET
1001490c8684SDarron Broad 		 *
1002490c8684SDarron Broad 		 *              WX= PORT SELECT 0..3    (X=TONEBURST)
1003490c8684SDarron Broad 		 *              Y = VOLTAGE             (0=13V, 1=18V)
1004490c8684SDarron Broad 		 *              Z = BAND                (0=LOW, 1=HIGH(22K))
1005490c8684SDarron Broad 		 */
10060d46748cSSteven Toth 		if (d->msg_len >= 4 && d->msg[2] == 0x38)
1007f11ec7d4SSteven Toth 			state->dsec_cmd.args[CX24116_DISEQC_BURST] =
1008f11ec7d4SSteven Toth 				((d->msg[3] & 4) >> 2);
1009490c8684SDarron Broad 		if (debug)
1010f11ec7d4SSteven Toth 			dprintk("%s burst=%d\n", __func__,
1011f11ec7d4SSteven Toth 				state->dsec_cmd.args[CX24116_DISEQC_BURST]);
1012490c8684SDarron Broad 	}
10130d46748cSSteven Toth 
1014490c8684SDarron Broad 	/* Wait for LNB ready */
1015490c8684SDarron Broad 	ret = cx24116_wait_for_lnb(fe);
1016490c8684SDarron Broad 	if (ret != 0)
1017490c8684SDarron Broad 		return ret;
10180d46748cSSteven Toth 
1019490c8684SDarron Broad 	/* Wait for voltage/min repeat delay */
1020490c8684SDarron Broad 	msleep(100);
10210d46748cSSteven Toth 
1022490c8684SDarron Broad 	/* Command */
1023490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
1024490c8684SDarron Broad 	if (ret != 0)
1025490c8684SDarron Broad 		return ret;
1026490c8684SDarron Broad 	/*
1027490c8684SDarron Broad 	 * Wait for send
10280d46748cSSteven Toth 	 *
10290d46748cSSteven Toth 	 * Eutelsat spec:
1030490c8684SDarron Broad 	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
10310d46748cSSteven Toth 	 *  13.5ms per byte     +
10320d46748cSSteven Toth 	 * >15ms delay          +
10330d46748cSSteven Toth 	 *  12.5ms burst        +
1034490c8684SDarron Broad 	 * >15ms delay            (XXX determine if FW does this, see set_tone)
10350d46748cSSteven Toth 	 */
1036f11ec7d4SSteven Toth 	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) +
1037f11ec7d4SSteven Toth 		((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60));
10380d46748cSSteven Toth 
1039490c8684SDarron Broad 	return 0;
10400d46748cSSteven Toth }
10410d46748cSSteven Toth 
10420d46748cSSteven Toth /* Send DiSEqC burst */
1043f11ec7d4SSteven Toth static int cx24116_diseqc_send_burst(struct dvb_frontend *fe,
10440df289a2SMauro Carvalho Chehab 	enum fe_sec_mini_cmd burst)
10450d46748cSSteven Toth {
10460d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
10470d46748cSSteven Toth 	int ret;
10480d46748cSSteven Toth 
1049490c8684SDarron Broad 	dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst);
10500d46748cSSteven Toth 
1051490c8684SDarron Broad 	/* DiSEqC burst */
10520d46748cSSteven Toth 	if (burst == SEC_MINI_A)
1053f11ec7d4SSteven Toth 		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
1054f11ec7d4SSteven Toth 			CX24116_DISEQC_MINI_A;
10550d46748cSSteven Toth 	else if (burst == SEC_MINI_B)
1056f11ec7d4SSteven Toth 		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
1057f11ec7d4SSteven Toth 			CX24116_DISEQC_MINI_B;
10580d46748cSSteven Toth 	else
10590d46748cSSteven Toth 		return -EINVAL;
10600d46748cSSteven Toth 
1061490c8684SDarron Broad 	/* DiSEqC toneburst */
1062490c8684SDarron Broad 	if (toneburst != CX24116_DISEQC_MESGCACHE)
1063490c8684SDarron Broad 		/* Burst is cached */
1064490c8684SDarron Broad 		return 0;
10650d46748cSSteven Toth 
1066490c8684SDarron Broad 	/* Burst is to be sent with cached message */
10670d46748cSSteven Toth 
1068490c8684SDarron Broad 	/* Wait for LNB ready */
1069490c8684SDarron Broad 	ret = cx24116_wait_for_lnb(fe);
1070490c8684SDarron Broad 	if (ret != 0)
10710d46748cSSteven Toth 		return ret;
1072490c8684SDarron Broad 
1073490c8684SDarron Broad 	/* Wait for voltage/min repeat delay */
1074490c8684SDarron Broad 	msleep(100);
1075490c8684SDarron Broad 
1076490c8684SDarron Broad 	/* Command */
1077490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
1078490c8684SDarron Broad 	if (ret != 0)
1079490c8684SDarron Broad 		return ret;
1080490c8684SDarron Broad 
1081490c8684SDarron Broad 	/*
1082490c8684SDarron Broad 	 * Wait for send
1083490c8684SDarron Broad 	 *
1084490c8684SDarron Broad 	 * Eutelsat spec:
1085490c8684SDarron Broad 	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
1086490c8684SDarron Broad 	 *  13.5ms per byte     +
1087490c8684SDarron Broad 	 * >15ms delay          +
1088490c8684SDarron Broad 	 *  12.5ms burst        +
1089490c8684SDarron Broad 	 * >15ms delay            (XXX determine if FW does this, see set_tone)
1090490c8684SDarron Broad 	 */
1091490c8684SDarron Broad 	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60);
1092490c8684SDarron Broad 
1093490c8684SDarron Broad 	return 0;
10940d46748cSSteven Toth }
10950d46748cSSteven Toth 
10960d46748cSSteven Toth static void cx24116_release(struct dvb_frontend *fe)
10970d46748cSSteven Toth {
10980d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
10990d46748cSSteven Toth 	dprintk("%s\n", __func__);
11000d46748cSSteven Toth 	kfree(state);
11010d46748cSSteven Toth }
11020d46748cSSteven Toth 
1103bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops;
11040d46748cSSteven Toth 
11050d46748cSSteven Toth struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
11060d46748cSSteven Toth 	struct i2c_adapter *i2c)
11070d46748cSSteven Toth {
1108d303b7c5SMarkus Elfring 	struct cx24116_state *state;
11090d46748cSSteven Toth 	int ret;
11100d46748cSSteven Toth 
11110d46748cSSteven Toth 	dprintk("%s\n", __func__);
11120d46748cSSteven Toth 
11130d46748cSSteven Toth 	/* allocate memory for the internal state */
11142d3da59fSMarkus Elfring 	state = kzalloc(sizeof(*state), GFP_KERNEL);
111598c94823SSteven Toth 	if (state == NULL)
11169722e569SMarkus Elfring 		return NULL;
11170d46748cSSteven Toth 
11180d46748cSSteven Toth 	state->config = config;
11190d46748cSSteven Toth 	state->i2c = i2c;
11200d46748cSSteven Toth 
11210d46748cSSteven Toth 	/* check if the demod is present */
112298c94823SSteven Toth 	ret = (cx24116_readreg(state, 0xFF) << 8) |
112398c94823SSteven Toth 		cx24116_readreg(state, 0xFE);
11240d46748cSSteven Toth 	if (ret != 0x0501) {
11259722e569SMarkus Elfring 		kfree(state);
112698c94823SSteven Toth 		printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n");
11279722e569SMarkus Elfring 		return NULL;
11280d46748cSSteven Toth 	}
11290d46748cSSteven Toth 
11300d46748cSSteven Toth 	/* create dvb_frontend */
113198c94823SSteven Toth 	memcpy(&state->frontend.ops, &cx24116_ops,
113298c94823SSteven Toth 		sizeof(struct dvb_frontend_ops));
11330d46748cSSteven Toth 	state->frontend.demodulator_priv = state;
11340d46748cSSteven Toth 	return &state->frontend;
11350d46748cSSteven Toth }
1136f11ec7d4SSteven Toth EXPORT_SYMBOL(cx24116_attach);
1137f11ec7d4SSteven Toth 
1138490c8684SDarron Broad /*
1139490c8684SDarron Broad  * Initialise or wake up device
1140490c8684SDarron Broad  *
1141490c8684SDarron Broad  * Power config will reset and load initial firmware if required
1142490c8684SDarron Broad  */
11430d46748cSSteven Toth static int cx24116_initfe(struct dvb_frontend *fe)
11440d46748cSSteven Toth {
1145490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
1146490c8684SDarron Broad 	struct cx24116_cmd cmd;
1147490c8684SDarron Broad 	int ret;
1148490c8684SDarron Broad 
11490d46748cSSteven Toth 	dprintk("%s()\n", __func__);
11500d46748cSSteven Toth 
1151490c8684SDarron Broad 	/* Power on */
1152490c8684SDarron Broad 	cx24116_writereg(state, 0xe0, 0);
1153490c8684SDarron Broad 	cx24116_writereg(state, 0xe1, 0);
1154490c8684SDarron Broad 	cx24116_writereg(state, 0xea, 0);
1155490c8684SDarron Broad 
1156490c8684SDarron Broad 	/* Firmware CMD 36: Power config */
1157490c8684SDarron Broad 	cmd.args[0x00] = CMD_TUNERSLEEP;
1158490c8684SDarron Broad 	cmd.args[0x01] = 0;
1159490c8684SDarron Broad 	cmd.len = 0x02;
1160490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &cmd);
1161490c8684SDarron Broad 	if (ret != 0)
1162490c8684SDarron Broad 		return ret;
1163490c8684SDarron Broad 
11648afe6ad6SIgor M. Liplianin 	ret = cx24116_diseqc_init(fe);
11658afe6ad6SIgor M. Liplianin 	if (ret != 0)
11668afe6ad6SIgor M. Liplianin 		return ret;
11678afe6ad6SIgor M. Liplianin 
11688afe6ad6SIgor M. Liplianin 	/* HVR-4000 needs this */
11698afe6ad6SIgor M. Liplianin 	return cx24116_set_voltage(fe, SEC_VOLTAGE_13);
11700d46748cSSteven Toth }
11710d46748cSSteven Toth 
1172490c8684SDarron Broad /*
1173490c8684SDarron Broad  * Put device to sleep
1174490c8684SDarron Broad  */
1175490c8684SDarron Broad static int cx24116_sleep(struct dvb_frontend *fe)
1176490c8684SDarron Broad {
1177490c8684SDarron Broad 	struct cx24116_state *state = fe->demodulator_priv;
1178490c8684SDarron Broad 	struct cx24116_cmd cmd;
1179490c8684SDarron Broad 	int ret;
1180490c8684SDarron Broad 
1181490c8684SDarron Broad 	dprintk("%s()\n", __func__);
1182490c8684SDarron Broad 
1183490c8684SDarron Broad 	/* Firmware CMD 36: Power config */
1184490c8684SDarron Broad 	cmd.args[0x00] = CMD_TUNERSLEEP;
1185490c8684SDarron Broad 	cmd.args[0x01] = 1;
1186490c8684SDarron Broad 	cmd.len = 0x02;
1187490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &cmd);
1188490c8684SDarron Broad 	if (ret != 0)
1189490c8684SDarron Broad 		return ret;
1190490c8684SDarron Broad 
1191490c8684SDarron Broad 	/* Power off (Shutdown clocks) */
1192490c8684SDarron Broad 	cx24116_writereg(state, 0xea, 0xff);
1193490c8684SDarron Broad 	cx24116_writereg(state, 0xe1, 1);
1194490c8684SDarron Broad 	cx24116_writereg(state, 0xe0, 1);
1195490c8684SDarron Broad 
1196490c8684SDarron Broad 	return 0;
1197490c8684SDarron Broad }
1198490c8684SDarron Broad 
11990d46748cSSteven Toth /* dvb-core told us to tune, the tv property cache will be complete,
12000d46748cSSteven Toth  * it's safe for is to pull values and use them for tuning purposes.
12010d46748cSSteven Toth  */
12021ac6a854SMauro Carvalho Chehab static int cx24116_set_frontend(struct dvb_frontend *fe)
12030d46748cSSteven Toth {
12040d46748cSSteven Toth 	struct cx24116_state *state = fe->demodulator_priv;
120556f0680aSSteven Toth 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
12060d46748cSSteven Toth 	struct cx24116_cmd cmd;
12070df289a2SMauro Carvalho Chehab 	enum fe_status tunerstat;
12082fd93396SDarron Broad 	int i, status, ret, retune = 1;
12090d46748cSSteven Toth 
12100d46748cSSteven Toth 	dprintk("%s()\n", __func__);
12110d46748cSSteven Toth 
1212490c8684SDarron Broad 	switch (c->delivery_system) {
1213490c8684SDarron Broad 	case SYS_DVBS:
1214490c8684SDarron Broad 		dprintk("%s: DVB-S delivery system selected\n", __func__);
121501a8f038SDarron Broad 
121601a8f038SDarron Broad 		/* Only QPSK is supported for DVB-S */
121701a8f038SDarron Broad 		if (c->modulation != QPSK) {
121801a8f038SDarron Broad 			dprintk("%s: unsupported modulation selected (%d)\n",
121901a8f038SDarron Broad 				__func__, c->modulation);
122001a8f038SDarron Broad 			return -EOPNOTSUPP;
122101a8f038SDarron Broad 		}
122201a8f038SDarron Broad 
122301a8f038SDarron Broad 		/* Pilot doesn't exist in DVB-S, turn bit off */
122401a8f038SDarron Broad 		state->dnxt.pilot_val = CX24116_PILOT_OFF;
122501a8f038SDarron Broad 
122601a8f038SDarron Broad 		/* DVB-S only supports 0.35 */
122701a8f038SDarron Broad 		if (c->rolloff != ROLLOFF_35) {
122801a8f038SDarron Broad 			dprintk("%s: unsupported rolloff selected (%d)\n",
122901a8f038SDarron Broad 				__func__, c->rolloff);
123001a8f038SDarron Broad 			return -EOPNOTSUPP;
123101a8f038SDarron Broad 		}
12327396d3eaSDarron Broad 		state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
1233490c8684SDarron Broad 		break;
123401a8f038SDarron Broad 
1235490c8684SDarron Broad 	case SYS_DVBS2:
1236490c8684SDarron Broad 		dprintk("%s: DVB-S2 delivery system selected\n", __func__);
123701a8f038SDarron Broad 
123801a8f038SDarron Broad 		/*
123901a8f038SDarron Broad 		 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
124001a8f038SDarron Broad 		 * but not hardware auto detection
124101a8f038SDarron Broad 		 */
12420a6393aeSSteven Toth 		if (c->modulation != PSK_8 && c->modulation != QPSK) {
124301a8f038SDarron Broad 			dprintk("%s: unsupported modulation selected (%d)\n",
124401a8f038SDarron Broad 				__func__, c->modulation);
124501a8f038SDarron Broad 			return -EOPNOTSUPP;
124601a8f038SDarron Broad 		}
124701a8f038SDarron Broad 
124801a8f038SDarron Broad 		switch (c->pilot) {
124901a8f038SDarron Broad 		case PILOT_AUTO:	/* Not supported but emulated */
125074563214SChristophe Thommeret 			state->dnxt.pilot_val = (c->modulation == QPSK)
125174563214SChristophe Thommeret 				? CX24116_PILOT_OFF : CX24116_PILOT_ON;
12522fd93396SDarron Broad 			retune++;
125374563214SChristophe Thommeret 			break;
125401a8f038SDarron Broad 		case PILOT_OFF:
125501a8f038SDarron Broad 			state->dnxt.pilot_val = CX24116_PILOT_OFF;
125601a8f038SDarron Broad 			break;
125701a8f038SDarron Broad 		case PILOT_ON:
125801a8f038SDarron Broad 			state->dnxt.pilot_val = CX24116_PILOT_ON;
125901a8f038SDarron Broad 			break;
126001a8f038SDarron Broad 		default:
126101a8f038SDarron Broad 			dprintk("%s: unsupported pilot mode selected (%d)\n",
126201a8f038SDarron Broad 				__func__, c->pilot);
126301a8f038SDarron Broad 			return -EOPNOTSUPP;
126401a8f038SDarron Broad 		}
126501a8f038SDarron Broad 
1266490c8684SDarron Broad 		switch (c->rolloff) {
1267490c8684SDarron Broad 		case ROLLOFF_20:
1268490c8684SDarron Broad 			state->dnxt.rolloff_val = CX24116_ROLLOFF_020;
1269490c8684SDarron Broad 			break;
1270490c8684SDarron Broad 		case ROLLOFF_25:
1271490c8684SDarron Broad 			state->dnxt.rolloff_val = CX24116_ROLLOFF_025;
1272490c8684SDarron Broad 			break;
1273490c8684SDarron Broad 		case ROLLOFF_35:
1274490c8684SDarron Broad 			state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
1275490c8684SDarron Broad 			break;
127601a8f038SDarron Broad 		case ROLLOFF_AUTO:	/* Rolloff must be explicit */
127701a8f038SDarron Broad 		default:
127801a8f038SDarron Broad 			dprintk("%s: unsupported rolloff selected (%d)\n",
127901a8f038SDarron Broad 				__func__, c->rolloff);
1280490c8684SDarron Broad 			return -EOPNOTSUPP;
1281490c8684SDarron Broad 		}
1282490c8684SDarron Broad 		break;
128301a8f038SDarron Broad 
1284490c8684SDarron Broad 	default:
1285490c8684SDarron Broad 		dprintk("%s: unsupported delivery system selected (%d)\n",
1286490c8684SDarron Broad 			__func__, c->delivery_system);
1287490c8684SDarron Broad 		return -EOPNOTSUPP;
1288490c8684SDarron Broad 	}
12893569476dSDarron Broad 	state->dnxt.delsys = c->delivery_system;
129001a8f038SDarron Broad 	state->dnxt.modulation = c->modulation;
129101a8f038SDarron Broad 	state->dnxt.frequency = c->frequency;
129201a8f038SDarron Broad 	state->dnxt.pilot = c->pilot;
129301a8f038SDarron Broad 	state->dnxt.rolloff = c->rolloff;
1294490c8684SDarron Broad 
1295f11ec7d4SSteven Toth 	ret = cx24116_set_inversion(state, c->inversion);
1296f11ec7d4SSteven Toth 	if (ret !=  0)
12970d46748cSSteven Toth 		return ret;
12980d46748cSSteven Toth 
129901a8f038SDarron Broad 	/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
13003569476dSDarron Broad 	ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner);
1301f11ec7d4SSteven Toth 	if (ret !=  0)
13020d46748cSSteven Toth 		return ret;
13030d46748cSSteven Toth 
1304f11ec7d4SSteven Toth 	ret = cx24116_set_symbolrate(state, c->symbol_rate);
1305f11ec7d4SSteven Toth 	if (ret !=  0)
13060d46748cSSteven Toth 		return ret;
13070d46748cSSteven Toth 
13080d46748cSSteven Toth 	/* discard the 'current' tuning parameters and prepare to tune */
13090d46748cSSteven Toth 	cx24116_clone_params(fe);
13100d46748cSSteven Toth 
13113569476dSDarron Broad 	dprintk("%s:   delsys      = %d\n", __func__, state->dcur.delsys);
131201a8f038SDarron Broad 	dprintk("%s:   modulation  = %d\n", __func__, state->dcur.modulation);
13130d46748cSSteven Toth 	dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);
131401a8f038SDarron Broad 	dprintk("%s:   pilot       = %d (val = 0x%02x)\n", __func__,
131501a8f038SDarron Broad 		state->dcur.pilot, state->dcur.pilot_val);
131601a8f038SDarron Broad 	dprintk("%s:   retune      = %d\n", __func__, retune);
131701a8f038SDarron Broad 	dprintk("%s:   rolloff     = %d (val = 0x%02x)\n", __func__,
131801a8f038SDarron Broad 		state->dcur.rolloff, state->dcur.rolloff_val);
13190d46748cSSteven Toth 	dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
13200d46748cSSteven Toth 	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
13210d46748cSSteven Toth 		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
13220d46748cSSteven Toth 	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __func__,
13230d46748cSSteven Toth 		state->dcur.inversion, state->dcur.inversion_val);
13240d46748cSSteven Toth 
1325490c8684SDarron Broad 	/* This is also done in advise/acquire on HVR4000 but not on LITE */
13260d46748cSSteven Toth 	if (state->config->set_ts_params)
13270d46748cSSteven Toth 		state->config->set_ts_params(fe, 0);
13280d46748cSSteven Toth 
1329490c8684SDarron Broad 	/* Set/Reset B/W */
1330490c8684SDarron Broad 	cmd.args[0x00] = CMD_BANDWIDTH;
1331490c8684SDarron Broad 	cmd.args[0x01] = 0x01;
1332490c8684SDarron Broad 	cmd.len = 0x02;
1333490c8684SDarron Broad 	ret = cx24116_cmd_execute(fe, &cmd);
1334490c8684SDarron Broad 	if (ret != 0)
1335490c8684SDarron Broad 		return ret;
13363f8e51adSIgor M. Liplianin 
13370d46748cSSteven Toth 	/* Prepare a tune request */
13380d46748cSSteven Toth 	cmd.args[0x00] = CMD_TUNEREQUEST;
13390d46748cSSteven Toth 
13400d46748cSSteven Toth 	/* Frequency */
13410d46748cSSteven Toth 	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
13420d46748cSSteven Toth 	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
13430d46748cSSteven Toth 	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
13440d46748cSSteven Toth 
13450d46748cSSteven Toth 	/* Symbol Rate */
13460d46748cSSteven Toth 	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
13470d46748cSSteven Toth 	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
13480d46748cSSteven Toth 
13490d46748cSSteven Toth 	/* Automatic Inversion */
13500d46748cSSteven Toth 	cmd.args[0x06] = state->dcur.inversion_val;
13510d46748cSSteven Toth 
135201a8f038SDarron Broad 	/* Modulation / FEC / Pilot */
135301a8f038SDarron Broad 	cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;
13540d46748cSSteven Toth 
13550d46748cSSteven Toth 	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
13560d46748cSSteven Toth 	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
13570d46748cSSteven Toth 	cmd.args[0x0a] = 0x00;
13580d46748cSSteven Toth 	cmd.args[0x0b] = 0x00;
1359490c8684SDarron Broad 	cmd.args[0x0c] = state->dcur.rolloff_val;
13600d46748cSSteven Toth 	cmd.args[0x0d] = state->dcur.fec_mask;
13613f8e51adSIgor M. Liplianin 
1362490c8684SDarron Broad 	if (state->dcur.symbol_rate > 30000000) {
13633f8e51adSIgor M. Liplianin 		cmd.args[0x0e] = 0x04;
13643f8e51adSIgor M. Liplianin 		cmd.args[0x0f] = 0x00;
13653f8e51adSIgor M. Liplianin 		cmd.args[0x10] = 0x01;
13663f8e51adSIgor M. Liplianin 		cmd.args[0x11] = 0x77;
13673f8e51adSIgor M. Liplianin 		cmd.args[0x12] = 0x36;
1368490c8684SDarron Broad 		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
1369490c8684SDarron Broad 		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
13703f8e51adSIgor M. Liplianin 	} else {
13710d46748cSSteven Toth 		cmd.args[0x0e] = 0x06;
13720d46748cSSteven Toth 		cmd.args[0x0f] = 0x00;
13730d46748cSSteven Toth 		cmd.args[0x10] = 0x00;
13740d46748cSSteven Toth 		cmd.args[0x11] = 0xFA;
13750d46748cSSteven Toth 		cmd.args[0x12] = 0x24;
1376490c8684SDarron Broad 		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
1377490c8684SDarron Broad 		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
13783f8e51adSIgor M. Liplianin 	}
13793f8e51adSIgor M. Liplianin 
13800d46748cSSteven Toth 	cmd.len = 0x13;
13810d46748cSSteven Toth 
13820d46748cSSteven Toth 	/* We need to support pilot and non-pilot tuning in the
13830d46748cSSteven Toth 	 * driver automatically. This is a workaround for because
13840d46748cSSteven Toth 	 * the demod does not support autodetect.
13850d46748cSSteven Toth 	 */
13860d46748cSSteven Toth 	do {
1387490c8684SDarron Broad 		/* Reset status register */
1388f11ec7d4SSteven Toth 		status = cx24116_readreg(state, CX24116_REG_SSTATUS)
1389f11ec7d4SSteven Toth 			& CX24116_SIGNAL_MASK;
1390490c8684SDarron Broad 		cx24116_writereg(state, CX24116_REG_SSTATUS, status);
13910d46748cSSteven Toth 
13920d46748cSSteven Toth 		/* Tune */
13930d46748cSSteven Toth 		ret = cx24116_cmd_execute(fe, &cmd);
13940d46748cSSteven Toth 		if (ret != 0)
13950d46748cSSteven Toth 			break;
13960d46748cSSteven Toth 
1397490c8684SDarron Broad 		/*
1398490c8684SDarron Broad 		 * Wait for up to 500 ms before retrying
1399490c8684SDarron Broad 		 *
1400490c8684SDarron Broad 		 * If we are able to tune then generally it occurs within 100ms.
1401490c8684SDarron Broad 		 * If it takes longer, try a different toneburst setting.
1402490c8684SDarron Broad 		 */
1403490c8684SDarron Broad 		for (i = 0; i < 50 ; i++) {
14040d46748cSSteven Toth 			cx24116_read_status(fe, &tunerstat);
1405490c8684SDarron Broad 			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
1406490c8684SDarron Broad 			if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
1407490c8684SDarron Broad 				dprintk("%s: Tuned\n", __func__);
1408490c8684SDarron Broad 				goto tuned;
14090d46748cSSteven Toth 			}
1410490c8684SDarron Broad 			msleep(10);
1411490c8684SDarron Broad 		}
1412490c8684SDarron Broad 
1413490c8684SDarron Broad 		dprintk("%s: Not tuned\n", __func__);
1414490c8684SDarron Broad 
1415490c8684SDarron Broad 		/* Toggle pilot bit when in auto-pilot */
1416490c8684SDarron Broad 		if (state->dcur.pilot == PILOT_AUTO)
141701a8f038SDarron Broad 			cmd.args[0x07] ^= CX24116_PILOT_ON;
1418f11ec7d4SSteven Toth 	} while (--retune);
14190d46748cSSteven Toth 
1420490c8684SDarron Broad tuned:  /* Set/Reset B/W */
1421490c8684SDarron Broad 	cmd.args[0x00] = CMD_BANDWIDTH;
1422490c8684SDarron Broad 	cmd.args[0x01] = 0x00;
1423490c8684SDarron Broad 	cmd.len = 0x02;
14243735edf9SGreg Dietsche 	return cx24116_cmd_execute(fe, &cmd);
14250d46748cSSteven Toth }
14260d46748cSSteven Toth 
14277e072221SMauro Carvalho Chehab static int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
14280df289a2SMauro Carvalho Chehab 	unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
14296639f1e0SDarron Broad {
14301ac6a854SMauro Carvalho Chehab 	/*
14311ac6a854SMauro Carvalho Chehab 	 * It is safe to discard "params" here, as the DVB core will sync
14321ac6a854SMauro Carvalho Chehab 	 * fe->dtv_property_cache with fepriv->parameters_in, where the
14331ac6a854SMauro Carvalho Chehab 	 * DVBv3 params are stored. The only practical usage for it indicate
14341ac6a854SMauro Carvalho Chehab 	 * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is
14351ac6a854SMauro Carvalho Chehab 	 * true.
14361ac6a854SMauro Carvalho Chehab 	 */
14371ac6a854SMauro Carvalho Chehab 
14386639f1e0SDarron Broad 	*delay = HZ / 5;
14397e072221SMauro Carvalho Chehab 	if (re_tune) {
14401ac6a854SMauro Carvalho Chehab 		int ret = cx24116_set_frontend(fe);
14416639f1e0SDarron Broad 		if (ret)
14426639f1e0SDarron Broad 			return ret;
14436639f1e0SDarron Broad 	}
14446639f1e0SDarron Broad 	return cx24116_read_status(fe, status);
14456639f1e0SDarron Broad }
14466639f1e0SDarron Broad 
14478d718e53SLuc Van Oostenryck static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe)
14486639f1e0SDarron Broad {
14496639f1e0SDarron Broad 	return DVBFE_ALGO_HW;
14506639f1e0SDarron Broad }
14516639f1e0SDarron Broad 
1452bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops = {
14531ac6a854SMauro Carvalho Chehab 	.delsys = { SYS_DVBS, SYS_DVBS2 },
14540d46748cSSteven Toth 	.info = {
14550d46748cSSteven Toth 		.name = "Conexant CX24116/CX24118",
1456f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz = 950 * MHz,
1457f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz = 2150 * MHz,
1458f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 1011 * kHz,
1459f1b1eabfSMauro Carvalho Chehab 		.frequency_tolerance_hz = 5 * MHz,
14600d46748cSSteven Toth 		.symbol_rate_min = 1000000,
14610d46748cSSteven Toth 		.symbol_rate_max = 45000000,
14620d46748cSSteven Toth 		.caps = FE_CAN_INVERSION_AUTO |
14630d46748cSSteven Toth 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
14640d46748cSSteven Toth 			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
14650d46748cSSteven Toth 			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
1466faed4aa5SKlaus Schmidinger 			FE_CAN_2G_MODULATION |
14670d46748cSSteven Toth 			FE_CAN_QPSK | FE_CAN_RECOVER
14680d46748cSSteven Toth 	},
14690d46748cSSteven Toth 
14700d46748cSSteven Toth 	.release = cx24116_release,
14710d46748cSSteven Toth 
14720d46748cSSteven Toth 	.init = cx24116_initfe,
1473490c8684SDarron Broad 	.sleep = cx24116_sleep,
14740d46748cSSteven Toth 	.read_status = cx24116_read_status,
14750d46748cSSteven Toth 	.read_ber = cx24116_read_ber,
14760d46748cSSteven Toth 	.read_signal_strength = cx24116_read_signal_strength,
14770d46748cSSteven Toth 	.read_snr = cx24116_read_snr,
14780d46748cSSteven Toth 	.read_ucblocks = cx24116_read_ucblocks,
14790d46748cSSteven Toth 	.set_tone = cx24116_set_tone,
14800d46748cSSteven Toth 	.set_voltage = cx24116_set_voltage,
14810d46748cSSteven Toth 	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
14820d46748cSSteven Toth 	.diseqc_send_burst = cx24116_diseqc_send_burst,
14836639f1e0SDarron Broad 	.get_frontend_algo = cx24116_get_algo,
14846639f1e0SDarron Broad 	.tune = cx24116_tune,
14850d46748cSSteven Toth 
14861ac6a854SMauro Carvalho Chehab 	.set_frontend = cx24116_set_frontend,
14870d46748cSSteven Toth };
14880d46748cSSteven Toth 
14890d46748cSSteven Toth MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
14900d46748cSSteven Toth MODULE_AUTHOR("Steven Toth");
14910d46748cSSteven Toth MODULE_LICENSE("GPL");
14920d46748cSSteven Toth 
1493