xref: /openbmc/linux/drivers/media/dvb-frontends/cx24120.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25afc9a25SJemma Denson /*
35afc9a25SJemma Denson     Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver
45afc9a25SJemma Denson 
55afc9a25SJemma Denson     Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
65afc9a25SJemma Denson     Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
75afc9a25SJemma Denson     Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
85afc9a25SJemma Denson     Copyright (C) 2015 Jemma Denson <jdenson@gmail.com>
95afc9a25SJemma Denson 	April 2015
105afc9a25SJemma Denson 	    Refactored & simplified driver
115afc9a25SJemma Denson 	    Updated to work with delivery system supplied by DVBv5
125afc9a25SJemma Denson 	    Add frequency, fec & pilot to get_frontend
135afc9a25SJemma Denson 
145afc9a25SJemma Denson 	Cards supported: Technisat Skystar S2
155afc9a25SJemma Denson 
165afc9a25SJemma Denson */
175afc9a25SJemma Denson 
185afc9a25SJemma Denson #include <linux/slab.h>
195afc9a25SJemma Denson #include <linux/kernel.h>
205afc9a25SJemma Denson #include <linux/module.h>
215afc9a25SJemma Denson #include <linux/moduleparam.h>
225afc9a25SJemma Denson #include <linux/init.h>
235afc9a25SJemma Denson #include <linux/firmware.h>
24fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
255afc9a25SJemma Denson #include "cx24120.h"
265afc9a25SJemma Denson 
275afc9a25SJemma Denson #define CX24120_SEARCH_RANGE_KHZ 5000
285afc9a25SJemma Denson #define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw"
295afc9a25SJemma Denson 
305afc9a25SJemma Denson /* cx24120 i2c registers  */
312e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_START	0x00		/* write cmd_id */
322e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_ARGS	0x01		/* write command arguments */
332e89a5e0SPatrick Boettcher #define CX24120_REG_CMD_END	0x1f		/* write 0x01 for end */
345afc9a25SJemma Denson 
352e89a5e0SPatrick Boettcher #define CX24120_REG_MAILBOX	0x33
362e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ3	0x34		/* frequency */
372e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ2	0x35
382e89a5e0SPatrick Boettcher #define CX24120_REG_FREQ1	0x36
395afc9a25SJemma Denson 
402e89a5e0SPatrick Boettcher #define CX24120_REG_FECMODE	0x39		/* FEC status */
412e89a5e0SPatrick Boettcher #define CX24120_REG_STATUS	0x3a		/* Tuner status */
422e89a5e0SPatrick Boettcher #define CX24120_REG_SIGSTR_H	0x3a		/* Signal strength high */
432e89a5e0SPatrick Boettcher #define CX24120_REG_SIGSTR_L	0x3b		/* Signal strength low byte */
442e89a5e0SPatrick Boettcher #define CX24120_REG_QUALITY_H	0x40		/* SNR high byte */
452e89a5e0SPatrick Boettcher #define CX24120_REG_QUALITY_L	0x41		/* SNR low byte */
465afc9a25SJemma Denson 
472e89a5e0SPatrick Boettcher #define CX24120_REG_BER_HH	0x47		/* BER high byte of high word */
482e89a5e0SPatrick Boettcher #define CX24120_REG_BER_HL	0x48		/* BER low byte of high word */
492e89a5e0SPatrick Boettcher #define CX24120_REG_BER_LH	0x49		/* BER high byte of low word */
502e89a5e0SPatrick Boettcher #define CX24120_REG_BER_LL	0x4a		/* BER low byte of low word */
515afc9a25SJemma Denson 
522e89a5e0SPatrick Boettcher #define CX24120_REG_UCB_H	0x50		/* UCB high byte */
532e89a5e0SPatrick Boettcher #define CX24120_REG_UCB_L	0x51		/* UCB low byte  */
545afc9a25SJemma Denson 
552e89a5e0SPatrick Boettcher #define CX24120_REG_CLKDIV	0xe6
562e89a5e0SPatrick Boettcher #define CX24120_REG_RATEDIV	0xf0
575afc9a25SJemma Denson 
582e89a5e0SPatrick Boettcher #define CX24120_REG_REVISION	0xff		/* Chip revision (ro) */
595afc9a25SJemma Denson 
605afc9a25SJemma Denson /* Command messages */
615afc9a25SJemma Denson enum command_message_id {
625afc9a25SJemma Denson 	CMD_VCO_SET		= 0x10,		/* cmd.len = 12; */
635afc9a25SJemma Denson 	CMD_TUNEREQUEST		= 0x11,		/* cmd.len = 15; */
645afc9a25SJemma Denson 
655afc9a25SJemma Denson 	CMD_MPEG_ONOFF		= 0x13,		/* cmd.len = 4; */
665afc9a25SJemma Denson 	CMD_MPEG_INIT		= 0x14,		/* cmd.len = 7; */
675afc9a25SJemma Denson 	CMD_BANDWIDTH		= 0x15,		/* cmd.len = 12; */
685afc9a25SJemma Denson 	CMD_CLOCK_READ		= 0x16,		/* read clock */
695afc9a25SJemma Denson 	CMD_CLOCK_SET		= 0x17,		/* cmd.len = 10; */
705afc9a25SJemma Denson 
715afc9a25SJemma Denson 	CMD_DISEQC_MSG1		= 0x20,		/* cmd.len = 11; */
725afc9a25SJemma Denson 	CMD_DISEQC_MSG2		= 0x21,		/* cmd.len = d->msg_len + 6; */
735afc9a25SJemma Denson 	CMD_SETVOLTAGE		= 0x22,		/* cmd.len = 2; */
745afc9a25SJemma Denson 	CMD_SETTONE		= 0x23,		/* cmd.len = 4; */
755afc9a25SJemma Denson 	CMD_DISEQC_BURST	= 0x24,		/* cmd.len not used !!! */
765afc9a25SJemma Denson 
775afc9a25SJemma Denson 	CMD_READ_SNR		= 0x1a,		/* Read signal strength */
785afc9a25SJemma Denson 	CMD_START_TUNER		= 0x1b,		/* ??? */
795afc9a25SJemma Denson 
805afc9a25SJemma Denson 	CMD_FWVERSION		= 0x35,
815afc9a25SJemma Denson 
82ddcb252eSJemma Denson 	CMD_BER_CTRL		= 0x3c,		/* cmd.len = 0x03; */
835afc9a25SJemma Denson };
845afc9a25SJemma Denson 
855afc9a25SJemma Denson #define CX24120_MAX_CMD_LEN	30
865afc9a25SJemma Denson 
875afc9a25SJemma Denson /* pilot mask */
882e89a5e0SPatrick Boettcher #define CX24120_PILOT_OFF	0x00
892e89a5e0SPatrick Boettcher #define CX24120_PILOT_ON	0x40
902e89a5e0SPatrick Boettcher #define CX24120_PILOT_AUTO	0x80
915afc9a25SJemma Denson 
925afc9a25SJemma Denson /* signal status */
932e89a5e0SPatrick Boettcher #define CX24120_HAS_SIGNAL	0x01
942e89a5e0SPatrick Boettcher #define CX24120_HAS_CARRIER	0x02
952e89a5e0SPatrick Boettcher #define CX24120_HAS_VITERBI	0x04
962e89a5e0SPatrick Boettcher #define CX24120_HAS_LOCK	0x08
972e89a5e0SPatrick Boettcher #define CX24120_HAS_UNK1	0x10
982e89a5e0SPatrick Boettcher #define CX24120_HAS_UNK2	0x20
992e89a5e0SPatrick Boettcher #define CX24120_STATUS_MASK	0x0f
1002e89a5e0SPatrick Boettcher #define CX24120_SIGNAL_MASK	0xc0
1015afc9a25SJemma Denson 
102ddcb252eSJemma Denson /* ber window */
103ddcb252eSJemma Denson #define CX24120_BER_WINDOW	16
104ddcb252eSJemma Denson #define CX24120_BER_WSIZE	((1 << CX24120_BER_WINDOW) * 208 * 8)
105ddcb252eSJemma Denson 
106c5fb0f5fSPatrick Boettcher #define info(args...) pr_info("cx24120: " args)
107c5fb0f5fSPatrick Boettcher #define err(args...)  pr_err("cx24120: ### ERROR: " args)
1085afc9a25SJemma Denson 
1095afc9a25SJemma Denson /* The Demod/Tuner can't easily provide these, we cache them */
1105afc9a25SJemma Denson struct cx24120_tuning {
1115afc9a25SJemma Denson 	u32 frequency;
1125afc9a25SJemma Denson 	u32 symbol_rate;
1130df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion;
1140df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
1155afc9a25SJemma Denson 
1160df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
1170df289a2SMauro Carvalho Chehab 	enum fe_modulation modulation;
1180df289a2SMauro Carvalho Chehab 	enum fe_pilot pilot;
1195afc9a25SJemma Denson 
1205afc9a25SJemma Denson 	/* Demod values */
1215afc9a25SJemma Denson 	u8 fec_val;
1225afc9a25SJemma Denson 	u8 fec_mask;
1235afc9a25SJemma Denson 	u8 clkdiv;
1245afc9a25SJemma Denson 	u8 ratediv;
1255afc9a25SJemma Denson 	u8 inversion_val;
1265afc9a25SJemma Denson 	u8 pilot_val;
1275afc9a25SJemma Denson };
1285afc9a25SJemma Denson 
1295afc9a25SJemma Denson /* Private state */
1305afc9a25SJemma Denson struct cx24120_state {
1315afc9a25SJemma Denson 	struct i2c_adapter *i2c;
1325afc9a25SJemma Denson 	const struct cx24120_config *config;
1335afc9a25SJemma Denson 	struct dvb_frontend frontend;
1345afc9a25SJemma Denson 
1355afc9a25SJemma Denson 	u8 cold_init;
1365afc9a25SJemma Denson 	u8 mpeg_enabled;
1376138dc2fSJemma Denson 	u8 need_clock_set;
1385afc9a25SJemma Denson 
1395afc9a25SJemma Denson 	/* current and next tuning parameters */
1405afc9a25SJemma Denson 	struct cx24120_tuning dcur;
1415afc9a25SJemma Denson 	struct cx24120_tuning dnxt;
1421462612cSJemma Denson 
1430df289a2SMauro Carvalho Chehab 	enum fe_status fe_status;
144ddcb252eSJemma Denson 
145c9adafa3SJemma Denson 	/* dvbv5 stats calculations */
14680e9710bSJemma Denson 	u32 bitrate;
147ddcb252eSJemma Denson 	u32 berw_usecs;
148fc443284SJemma Denson 	u32 ber_prev;
149bf8de2d3SJemma Denson 	u32 ucb_offset;
150ddcb252eSJemma Denson 	unsigned long ber_jiffies_stats;
15180e9710bSJemma Denson 	unsigned long per_jiffies_stats;
1525afc9a25SJemma Denson };
1535afc9a25SJemma Denson 
1545afc9a25SJemma Denson /* Command message to firmware */
1555afc9a25SJemma Denson struct cx24120_cmd {
1565afc9a25SJemma Denson 	u8 id;
1575afc9a25SJemma Denson 	u8 len;
1585afc9a25SJemma Denson 	u8 arg[CX24120_MAX_CMD_LEN];
1595afc9a25SJemma Denson };
1605afc9a25SJemma Denson 
1615afc9a25SJemma Denson /* Read single register */
cx24120_readreg(struct cx24120_state * state,u8 reg)1625afc9a25SJemma Denson static int cx24120_readreg(struct cx24120_state *state, u8 reg)
1635afc9a25SJemma Denson {
1645afc9a25SJemma Denson 	int ret;
1655afc9a25SJemma Denson 	u8 buf = 0;
1665afc9a25SJemma Denson 	struct i2c_msg msg[] = {
167fbdbab72SJemma Denson 		{
168fbdbab72SJemma Denson 			.addr = state->config->i2c_addr,
1695afc9a25SJemma Denson 			.flags = 0,
1705afc9a25SJemma Denson 			.len = 1,
1712e89a5e0SPatrick Boettcher 			.buf = &reg
1722e89a5e0SPatrick Boettcher 		}, {
1732e89a5e0SPatrick Boettcher 			.addr = state->config->i2c_addr,
1745afc9a25SJemma Denson 			.flags = I2C_M_RD,
1755afc9a25SJemma Denson 			.len = 1,
1762e89a5e0SPatrick Boettcher 			.buf = &buf
1772e89a5e0SPatrick Boettcher 		}
1785afc9a25SJemma Denson 	};
1792e89a5e0SPatrick Boettcher 
1805afc9a25SJemma Denson 	ret = i2c_transfer(state->i2c, msg, 2);
1815afc9a25SJemma Denson 	if (ret != 2) {
1822e89a5e0SPatrick Boettcher 		err("Read error: reg=0x%02x, ret=%i)\n", reg, ret);
1835afc9a25SJemma Denson 		return ret;
1845afc9a25SJemma Denson 	}
1855afc9a25SJemma Denson 
1862f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf);
1875afc9a25SJemma Denson 
1885afc9a25SJemma Denson 	return buf;
1895afc9a25SJemma Denson }
1905afc9a25SJemma Denson 
1915afc9a25SJemma Denson /* Write single register */
cx24120_writereg(struct cx24120_state * state,u8 reg,u8 data)1925afc9a25SJemma Denson static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data)
1935afc9a25SJemma Denson {
1945afc9a25SJemma Denson 	u8 buf[] = { reg, data };
1955afc9a25SJemma Denson 	struct i2c_msg msg = {
1965afc9a25SJemma Denson 		.addr = state->config->i2c_addr,
1975afc9a25SJemma Denson 		.flags = 0,
1985afc9a25SJemma Denson 		.buf = buf,
1992e89a5e0SPatrick Boettcher 		.len = 2
2002e89a5e0SPatrick Boettcher 	};
2015afc9a25SJemma Denson 	int ret;
2025afc9a25SJemma Denson 
2035afc9a25SJemma Denson 	ret = i2c_transfer(state->i2c, &msg, 1);
2045afc9a25SJemma Denson 	if (ret != 1) {
2055afc9a25SJemma Denson 		err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n",
2065afc9a25SJemma Denson 		    ret, reg, data);
2075afc9a25SJemma Denson 		return ret;
2085afc9a25SJemma Denson 	}
2095afc9a25SJemma Denson 
2102f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data);
2115afc9a25SJemma Denson 
2125afc9a25SJemma Denson 	return 0;
2135afc9a25SJemma Denson }
2145afc9a25SJemma Denson 
215f7a77ebfSPatrick Boettcher /* Write multiple registers in chunks of i2c_wr_max-sized buffers */
cx24120_writeregs(struct cx24120_state * state,u8 reg,const u8 * values,u16 len,u8 incr)2161ff2e8edSPatrick Boettcher static int cx24120_writeregs(struct cx24120_state *state,
2175afc9a25SJemma Denson 			     u8 reg, const u8 *values, u16 len, u8 incr)
2185afc9a25SJemma Denson {
2195afc9a25SJemma Denson 	int ret;
220f7a77ebfSPatrick Boettcher 	u16 max = state->config->i2c_wr_max > 0 ?
221f7a77ebfSPatrick Boettcher 				state->config->i2c_wr_max :
222f7a77ebfSPatrick Boettcher 				len;
2235afc9a25SJemma Denson 
2245afc9a25SJemma Denson 	struct i2c_msg msg = {
2255afc9a25SJemma Denson 		.addr = state->config->i2c_addr,
2265afc9a25SJemma Denson 		.flags = 0,
227f7a77ebfSPatrick Boettcher 	};
228f7a77ebfSPatrick Boettcher 
229f7a77ebfSPatrick Boettcher 	msg.buf = kmalloc(max + 1, GFP_KERNEL);
2301ff2e8edSPatrick Boettcher 	if (!msg.buf)
231f7a77ebfSPatrick Boettcher 		return -ENOMEM;
2325afc9a25SJemma Denson 
2335afc9a25SJemma Denson 	while (len) {
234f7a77ebfSPatrick Boettcher 		msg.buf[0] = reg;
235f7a77ebfSPatrick Boettcher 		msg.len = len > max ? max : len;
236f7a77ebfSPatrick Boettcher 		memcpy(&msg.buf[1], values, msg.len);
2375afc9a25SJemma Denson 
2385afc9a25SJemma Denson 		len    -= msg.len;      /* data length revers counter */
2395afc9a25SJemma Denson 		values += msg.len;      /* incr data pointer */
2405afc9a25SJemma Denson 
2415afc9a25SJemma Denson 		if (incr)
2425afc9a25SJemma Denson 			reg += msg.len;
2435afc9a25SJemma Denson 		msg.len++;              /* don't forget the addr byte */
2445afc9a25SJemma Denson 
2455afc9a25SJemma Denson 		ret = i2c_transfer(state->i2c, &msg, 1);
2465afc9a25SJemma Denson 		if (ret != 1) {
2475afc9a25SJemma Denson 			err("i2c_write error(err == %i, 0x%02x)\n", ret, reg);
248f7a77ebfSPatrick Boettcher 			goto out;
2495afc9a25SJemma Denson 		}
2505afc9a25SJemma Denson 
2512f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n",
2522f3f07fbSJemma Denson 			reg, msg.len - 1, msg.buf + 1);
2535afc9a25SJemma Denson 	}
2545afc9a25SJemma Denson 
255f7a77ebfSPatrick Boettcher 	ret = 0;
256f7a77ebfSPatrick Boettcher 
257f7a77ebfSPatrick Boettcher out:
258f7a77ebfSPatrick Boettcher 	kfree(msg.buf);
259f7a77ebfSPatrick Boettcher 	return ret;
2605afc9a25SJemma Denson }
2615afc9a25SJemma Denson 
262bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24120_ops;
2635afc9a25SJemma Denson 
cx24120_attach(const struct cx24120_config * config,struct i2c_adapter * i2c)2645afc9a25SJemma Denson struct dvb_frontend *cx24120_attach(const struct cx24120_config *config,
2655afc9a25SJemma Denson 				    struct i2c_adapter *i2c)
2665afc9a25SJemma Denson {
2671ff2e8edSPatrick Boettcher 	struct cx24120_state *state;
2685afc9a25SJemma Denson 	int demod_rev;
2695afc9a25SJemma Denson 
2705afc9a25SJemma Denson 	info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n");
2711ff2e8edSPatrick Boettcher 	state = kzalloc(sizeof(*state), GFP_KERNEL);
2721ff2e8edSPatrick Boettcher 	if (!state) {
2735afc9a25SJemma Denson 		err("Unable to allocate memory for cx24120_state\n");
2745afc9a25SJemma Denson 		goto error;
2755afc9a25SJemma Denson 	}
2765afc9a25SJemma Denson 
2775afc9a25SJemma Denson 	/* setup the state */
2785afc9a25SJemma Denson 	state->config = config;
2795afc9a25SJemma Denson 	state->i2c = i2c;
2805afc9a25SJemma Denson 
2815afc9a25SJemma Denson 	/* check if the demod is present and has proper type */
2825afc9a25SJemma Denson 	demod_rev = cx24120_readreg(state, CX24120_REG_REVISION);
2835afc9a25SJemma Denson 	switch (demod_rev) {
2845afc9a25SJemma Denson 	case 0x07:
2855afc9a25SJemma Denson 		info("Demod cx24120 rev. 0x07 detected.\n");
2865afc9a25SJemma Denson 		break;
2875afc9a25SJemma Denson 	case 0x05:
2885afc9a25SJemma Denson 		info("Demod cx24120 rev. 0x05 detected.\n");
2895afc9a25SJemma Denson 		break;
2905afc9a25SJemma Denson 	default:
2911ff2e8edSPatrick Boettcher 		err("Unsupported demod revision: 0x%x detected.\n", demod_rev);
2925afc9a25SJemma Denson 		goto error;
2935afc9a25SJemma Denson 	}
2945afc9a25SJemma Denson 
2955afc9a25SJemma Denson 	/* create dvb_frontend */
2965afc9a25SJemma Denson 	state->cold_init = 0;
2975afc9a25SJemma Denson 	memcpy(&state->frontend.ops, &cx24120_ops,
2985afc9a25SJemma Denson 	       sizeof(struct dvb_frontend_ops));
2995afc9a25SJemma Denson 	state->frontend.demodulator_priv = state;
3005afc9a25SJemma Denson 
3015afc9a25SJemma Denson 	info("Conexant cx24120/cx24118 attached.\n");
3025afc9a25SJemma Denson 	return &state->frontend;
3035afc9a25SJemma Denson 
3045afc9a25SJemma Denson error:
3055afc9a25SJemma Denson 	kfree(state);
3065afc9a25SJemma Denson 	return NULL;
3075afc9a25SJemma Denson }
308*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(cx24120_attach);
3095afc9a25SJemma Denson 
cx24120_test_rom(struct cx24120_state * state)3105afc9a25SJemma Denson static int cx24120_test_rom(struct cx24120_state *state)
3115afc9a25SJemma Denson {
3125afc9a25SJemma Denson 	int err, ret;
3135afc9a25SJemma Denson 
3145afc9a25SJemma Denson 	err = cx24120_readreg(state, 0xfd);
3155afc9a25SJemma Denson 	if (err & 4) {
3165afc9a25SJemma Denson 		ret = cx24120_readreg(state, 0xdf) & 0xfe;
3175afc9a25SJemma Denson 		err = cx24120_writereg(state, 0xdf, ret);
3185afc9a25SJemma Denson 	}
3195afc9a25SJemma Denson 	return err;
3205afc9a25SJemma Denson }
3215afc9a25SJemma Denson 
cx24120_read_snr(struct dvb_frontend * fe,u16 * snr)3225afc9a25SJemma Denson static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr)
3235afc9a25SJemma Denson {
3243b5eb504SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
3255afc9a25SJemma Denson 
3263b5eb504SJemma Denson 	if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL)
3273b5eb504SJemma Denson 		*snr = 0;
3283b5eb504SJemma Denson 	else
3293b5eb504SJemma Denson 		*snr = div_s64(c->cnr.stat[0].svalue, 100);
3305afc9a25SJemma Denson 
3315afc9a25SJemma Denson 	return 0;
3325afc9a25SJemma Denson }
3335afc9a25SJemma Denson 
cx24120_read_ber(struct dvb_frontend * fe,u32 * ber)3345afc9a25SJemma Denson static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber)
3355afc9a25SJemma Denson {
3365afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
337fc443284SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
3385afc9a25SJemma Denson 
339fc443284SJemma Denson 	if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) {
340fc443284SJemma Denson 		*ber = 0;
341fc443284SJemma Denson 		return 0;
342fc443284SJemma Denson 	}
343fc443284SJemma Denson 
344fc443284SJemma Denson 	*ber = c->post_bit_error.stat[0].uvalue - state->ber_prev;
345fc443284SJemma Denson 	state->ber_prev = c->post_bit_error.stat[0].uvalue;
3465afc9a25SJemma Denson 
3475afc9a25SJemma Denson 	return 0;
3485afc9a25SJemma Denson }
3495afc9a25SJemma Denson 
3505afc9a25SJemma Denson static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
3515afc9a25SJemma Denson 						 u8 flag);
3525afc9a25SJemma Denson 
3535afc9a25SJemma Denson /* Check if we're running a command that needs to disable mpeg out */
cx24120_check_cmd(struct cx24120_state * state,u8 id)3545afc9a25SJemma Denson static void cx24120_check_cmd(struct cx24120_state *state, u8 id)
3555afc9a25SJemma Denson {
3565afc9a25SJemma Denson 	switch (id) {
3575afc9a25SJemma Denson 	case CMD_TUNEREQUEST:
3585afc9a25SJemma Denson 	case CMD_CLOCK_READ:
3595afc9a25SJemma Denson 	case CMD_DISEQC_MSG1:
3605afc9a25SJemma Denson 	case CMD_DISEQC_MSG2:
3615afc9a25SJemma Denson 	case CMD_SETVOLTAGE:
3625afc9a25SJemma Denson 	case CMD_SETTONE:
363270e7071SJemma Denson 	case CMD_DISEQC_BURST:
3645afc9a25SJemma Denson 		cx24120_msg_mpeg_output_global_config(state, 0);
3655afc9a25SJemma Denson 		/* Old driver would do a msleep(100) here */
366af7ab662SGustavo A. R. Silva 		return;
3675afc9a25SJemma Denson 	default:
3685afc9a25SJemma Denson 		return;
3695afc9a25SJemma Denson 	}
3705afc9a25SJemma Denson }
3715afc9a25SJemma Denson 
3725afc9a25SJemma Denson /* Send a message to the firmware */
cx24120_message_send(struct cx24120_state * state,struct cx24120_cmd * cmd)3735afc9a25SJemma Denson static int cx24120_message_send(struct cx24120_state *state,
3745afc9a25SJemma Denson 				struct cx24120_cmd *cmd)
3755afc9a25SJemma Denson {
37665b01665SMauro Carvalho Chehab 	int ficus;
3775afc9a25SJemma Denson 
3785afc9a25SJemma Denson 	if (state->mpeg_enabled) {
3795afc9a25SJemma Denson 		/* Disable mpeg out on certain commands */
3805afc9a25SJemma Denson 		cx24120_check_cmd(state, cmd->id);
3815afc9a25SJemma Denson 	}
3825afc9a25SJemma Denson 
38365b01665SMauro Carvalho Chehab 	cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id);
38465b01665SMauro Carvalho Chehab 	cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0],
3855afc9a25SJemma Denson 			  cmd->len, 1);
38665b01665SMauro Carvalho Chehab 	cx24120_writereg(state, CX24120_REG_CMD_END, 0x01);
3875afc9a25SJemma Denson 
3885afc9a25SJemma Denson 	ficus = 1000;
3895afc9a25SJemma Denson 	while (cx24120_readreg(state, CX24120_REG_CMD_END)) {
3905afc9a25SJemma Denson 		msleep(20);
3915afc9a25SJemma Denson 		ficus -= 20;
3925afc9a25SJemma Denson 		if (ficus == 0) {
3935afc9a25SJemma Denson 			err("Error sending message to firmware\n");
3945afc9a25SJemma Denson 			return -EREMOTEIO;
3955afc9a25SJemma Denson 		}
3965afc9a25SJemma Denson 	}
3972f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id);
3985afc9a25SJemma Denson 
3995afc9a25SJemma Denson 	return 0;
4005afc9a25SJemma Denson }
4015afc9a25SJemma Denson 
4025afc9a25SJemma Denson /* Send a message and fill arg[] with the results */
cx24120_message_sendrcv(struct cx24120_state * state,struct cx24120_cmd * cmd,u8 numreg)4035afc9a25SJemma Denson static int cx24120_message_sendrcv(struct cx24120_state *state,
4045afc9a25SJemma Denson 				   struct cx24120_cmd *cmd, u8 numreg)
4055afc9a25SJemma Denson {
4065afc9a25SJemma Denson 	int ret, i;
4075afc9a25SJemma Denson 
4085afc9a25SJemma Denson 	if (numreg > CX24120_MAX_CMD_LEN) {
4095afc9a25SJemma Denson 		err("Too many registers to read. cmd->reg = %d", numreg);
4105afc9a25SJemma Denson 		return -EREMOTEIO;
4115afc9a25SJemma Denson 	}
4125afc9a25SJemma Denson 
4135afc9a25SJemma Denson 	ret = cx24120_message_send(state, cmd);
4145afc9a25SJemma Denson 	if (ret != 0)
4155afc9a25SJemma Denson 		return ret;
4165afc9a25SJemma Denson 
4175afc9a25SJemma Denson 	if (!numreg)
4185afc9a25SJemma Denson 		return 0;
4195afc9a25SJemma Denson 
4205afc9a25SJemma Denson 	/* Read numreg registers starting from register cmd->len */
4215afc9a25SJemma Denson 	for (i = 0; i < numreg; i++)
4225afc9a25SJemma Denson 		cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1));
4235afc9a25SJemma Denson 
4245afc9a25SJemma Denson 	return 0;
4255afc9a25SJemma Denson }
4265afc9a25SJemma Denson 
cx24120_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)4275afc9a25SJemma Denson static int cx24120_read_signal_strength(struct dvb_frontend *fe,
4285afc9a25SJemma Denson 					u16 *signal_strength)
4295afc9a25SJemma Denson {
43034ce475dSJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
4315afc9a25SJemma Denson 
43234ce475dSJemma Denson 	if (c->strength.stat[0].scale != FE_SCALE_RELATIVE)
43334ce475dSJemma Denson 		*signal_strength = 0;
43434ce475dSJemma Denson 	else
43534ce475dSJemma Denson 		*signal_strength = c->strength.stat[0].uvalue;
4365afc9a25SJemma Denson 
4375afc9a25SJemma Denson 	return 0;
4385afc9a25SJemma Denson }
4395afc9a25SJemma Denson 
cx24120_msg_mpeg_output_global_config(struct cx24120_state * state,u8 enable)4405afc9a25SJemma Denson static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
4415afc9a25SJemma Denson 						 u8 enable)
4425afc9a25SJemma Denson {
4435afc9a25SJemma Denson 	struct cx24120_cmd cmd;
4445afc9a25SJemma Denson 	int ret;
4455afc9a25SJemma Denson 
4465afc9a25SJemma Denson 	cmd.id = CMD_MPEG_ONOFF;
4475afc9a25SJemma Denson 	cmd.len = 4;
4485afc9a25SJemma Denson 	cmd.arg[0] = 0x01;
4495afc9a25SJemma Denson 	cmd.arg[1] = 0x00;
4505afc9a25SJemma Denson 	cmd.arg[2] = enable ? 0 : (u8)(-1);
4515afc9a25SJemma Denson 	cmd.arg[3] = 0x01;
4525afc9a25SJemma Denson 
4535afc9a25SJemma Denson 	ret = cx24120_message_send(state, &cmd);
4545afc9a25SJemma Denson 	if (ret != 0) {
4552f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n",
4562f3f07fbSJemma Denson 			enable ? "enable" : "disable");
4575afc9a25SJemma Denson 		return ret;
4585afc9a25SJemma Denson 	}
4595afc9a25SJemma Denson 
4605afc9a25SJemma Denson 	state->mpeg_enabled = enable;
4612f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "MPEG output %s\n",
4622f3f07fbSJemma Denson 		enable ? "enabled" : "disabled");
4635afc9a25SJemma Denson 
4645afc9a25SJemma Denson 	return 0;
4655afc9a25SJemma Denson }
4665afc9a25SJemma Denson 
cx24120_msg_mpeg_output_config(struct cx24120_state * state,u8 seq)4675afc9a25SJemma Denson static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq)
4685afc9a25SJemma Denson {
4695afc9a25SJemma Denson 	struct cx24120_cmd cmd;
4705afc9a25SJemma Denson 	struct cx24120_initial_mpeg_config i =
4715afc9a25SJemma Denson 			state->config->initial_mpeg_config;
4725afc9a25SJemma Denson 
4735afc9a25SJemma Denson 	cmd.id = CMD_MPEG_INIT;
4745afc9a25SJemma Denson 	cmd.len = 7;
4755afc9a25SJemma Denson 	cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */
4765afc9a25SJemma Denson 	cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01);
4775afc9a25SJemma Denson 	cmd.arg[2] = 0x05;
4785afc9a25SJemma Denson 	cmd.arg[3] = 0x02;
4795afc9a25SJemma Denson 	cmd.arg[4] = ((i.x2 >> 1) & 0x01);
4805afc9a25SJemma Denson 	cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f);
4815afc9a25SJemma Denson 	cmd.arg[6] = 0x10;
4825afc9a25SJemma Denson 
4835afc9a25SJemma Denson 	return cx24120_message_send(state, &cmd);
4845afc9a25SJemma Denson }
4855afc9a25SJemma Denson 
cx24120_diseqc_send_burst(struct dvb_frontend * fe,enum fe_sec_mini_cmd burst)4865afc9a25SJemma Denson static int cx24120_diseqc_send_burst(struct dvb_frontend *fe,
4870df289a2SMauro Carvalho Chehab 				     enum fe_sec_mini_cmd burst)
4885afc9a25SJemma Denson {
4895afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
4905afc9a25SJemma Denson 	struct cx24120_cmd cmd;
4915afc9a25SJemma Denson 
4922f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "\n");
4932f3f07fbSJemma Denson 
494fbdbab72SJemma Denson 	/*
495fbdbab72SJemma Denson 	 * Yes, cmd.len is set to zero. The old driver
4965afc9a25SJemma Denson 	 * didn't specify any len, but also had a
4975afc9a25SJemma Denson 	 * memset 0 before every use of the cmd struct
4985afc9a25SJemma Denson 	 * which would have set it to zero.
4995afc9a25SJemma Denson 	 * This quite probably needs looking into.
5005afc9a25SJemma Denson 	 */
5015afc9a25SJemma Denson 	cmd.id = CMD_DISEQC_BURST;
5025afc9a25SJemma Denson 	cmd.len = 0;
5035afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
5047c95e25eSJemma Denson 	cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00;
5051ff2e8edSPatrick Boettcher 
5065afc9a25SJemma Denson 	return cx24120_message_send(state, &cmd);
5075afc9a25SJemma Denson }
5085afc9a25SJemma Denson 
cx24120_set_tone(struct dvb_frontend * fe,enum fe_sec_tone_mode tone)5090df289a2SMauro Carvalho Chehab static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
5105afc9a25SJemma Denson {
5115afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
5125afc9a25SJemma Denson 	struct cx24120_cmd cmd;
5135afc9a25SJemma Denson 
5142f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", tone);
5155afc9a25SJemma Denson 
5165afc9a25SJemma Denson 	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
5175afc9a25SJemma Denson 		err("Invalid tone=%d\n", tone);
5185afc9a25SJemma Denson 		return -EINVAL;
5195afc9a25SJemma Denson 	}
5205afc9a25SJemma Denson 
5215afc9a25SJemma Denson 	cmd.id = CMD_SETTONE;
5225afc9a25SJemma Denson 	cmd.len = 4;
5235afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
5245afc9a25SJemma Denson 	cmd.arg[1] = 0x00;
5255afc9a25SJemma Denson 	cmd.arg[2] = 0x00;
5265afc9a25SJemma Denson 	cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00;
5275afc9a25SJemma Denson 
5285afc9a25SJemma Denson 	return cx24120_message_send(state, &cmd);
5295afc9a25SJemma Denson }
5305afc9a25SJemma Denson 
cx24120_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage voltage)5315afc9a25SJemma Denson static int cx24120_set_voltage(struct dvb_frontend *fe,
5320df289a2SMauro Carvalho Chehab 			       enum fe_sec_voltage voltage)
5335afc9a25SJemma Denson {
5345afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
5355afc9a25SJemma Denson 	struct cx24120_cmd cmd;
5365afc9a25SJemma Denson 
5372f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", voltage);
5385afc9a25SJemma Denson 
5395afc9a25SJemma Denson 	cmd.id = CMD_SETVOLTAGE;
5405afc9a25SJemma Denson 	cmd.len = 2;
5415afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
5425afc9a25SJemma Denson 	cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00;
5435afc9a25SJemma Denson 
5445afc9a25SJemma Denson 	return cx24120_message_send(state, &cmd);
5455afc9a25SJemma Denson }
5465afc9a25SJemma Denson 
cx24120_send_diseqc_msg(struct dvb_frontend * fe,struct dvb_diseqc_master_cmd * d)5475afc9a25SJemma Denson static int cx24120_send_diseqc_msg(struct dvb_frontend *fe,
5485afc9a25SJemma Denson 				   struct dvb_diseqc_master_cmd *d)
5495afc9a25SJemma Denson {
5505afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
5515afc9a25SJemma Denson 	struct cx24120_cmd cmd;
5525afc9a25SJemma Denson 	int back_count;
5535afc9a25SJemma Denson 
5542f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "\n");
5555afc9a25SJemma Denson 
5565afc9a25SJemma Denson 	cmd.id = CMD_DISEQC_MSG1;
5575afc9a25SJemma Denson 	cmd.len = 11;
5585afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
5595afc9a25SJemma Denson 	cmd.arg[1] = 0x00;
5605afc9a25SJemma Denson 	cmd.arg[2] = 0x03;
5615afc9a25SJemma Denson 	cmd.arg[3] = 0x16;
5625afc9a25SJemma Denson 	cmd.arg[4] = 0x28;
5635afc9a25SJemma Denson 	cmd.arg[5] = 0x01;
5645afc9a25SJemma Denson 	cmd.arg[6] = 0x01;
5655afc9a25SJemma Denson 	cmd.arg[7] = 0x14;
5665afc9a25SJemma Denson 	cmd.arg[8] = 0x19;
5675afc9a25SJemma Denson 	cmd.arg[9] = 0x14;
5685afc9a25SJemma Denson 	cmd.arg[10] = 0x1e;
5695afc9a25SJemma Denson 
5705afc9a25SJemma Denson 	if (cx24120_message_send(state, &cmd)) {
5715afc9a25SJemma Denson 		err("send 1st message(0x%x) failed\n", cmd.id);
5725afc9a25SJemma Denson 		return -EREMOTEIO;
5735afc9a25SJemma Denson 	}
5745afc9a25SJemma Denson 
5755afc9a25SJemma Denson 	cmd.id = CMD_DISEQC_MSG2;
5765afc9a25SJemma Denson 	cmd.len = d->msg_len + 6;
5775afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
5785afc9a25SJemma Denson 	cmd.arg[1] = 0x01;
5795afc9a25SJemma Denson 	cmd.arg[2] = 0x02;
5805afc9a25SJemma Denson 	cmd.arg[3] = 0x00;
5815afc9a25SJemma Denson 	cmd.arg[4] = 0x00;
5825afc9a25SJemma Denson 	cmd.arg[5] = d->msg_len;
5835afc9a25SJemma Denson 
5845afc9a25SJemma Denson 	memcpy(&cmd.arg[6], &d->msg, d->msg_len);
5855afc9a25SJemma Denson 
5865afc9a25SJemma Denson 	if (cx24120_message_send(state, &cmd)) {
5875afc9a25SJemma Denson 		err("send 2nd message(0x%x) failed\n", cmd.id);
5885afc9a25SJemma Denson 		return -EREMOTEIO;
5895afc9a25SJemma Denson 	}
5905afc9a25SJemma Denson 
5915afc9a25SJemma Denson 	back_count = 500;
5925afc9a25SJemma Denson 	do {
5935afc9a25SJemma Denson 		if (!(cx24120_readreg(state, 0x93) & 0x01)) {
5942f3f07fbSJemma Denson 			dev_dbg(&state->i2c->dev, "diseqc sequence sent\n");
5955afc9a25SJemma Denson 			return 0;
5965afc9a25SJemma Denson 		}
5975afc9a25SJemma Denson 		msleep(20);
5985afc9a25SJemma Denson 		back_count -= 20;
5995afc9a25SJemma Denson 	} while (back_count);
6005afc9a25SJemma Denson 
6015afc9a25SJemma Denson 	err("Too long waiting for diseqc.\n");
6025afc9a25SJemma Denson 	return -ETIMEDOUT;
6035afc9a25SJemma Denson }
6045afc9a25SJemma Denson 
cx24120_get_stats(struct cx24120_state * state)6051462612cSJemma Denson static void cx24120_get_stats(struct cx24120_state *state)
6069fc18f18SJemma Denson {
6079fc18f18SJemma Denson 	struct dvb_frontend *fe = &state->frontend;
6089fc18f18SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
60934ce475dSJemma Denson 	struct cx24120_cmd cmd;
610ddcb252eSJemma Denson 	int ret, cnr, msecs;
61180e9710bSJemma Denson 	u16 sig, ucb;
612ddcb252eSJemma Denson 	u32 ber;
6139fc18f18SJemma Denson 
6142f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "\n");
6159fc18f18SJemma Denson 
6169fc18f18SJemma Denson 	/* signal strength */
6171462612cSJemma Denson 	if (state->fe_status & FE_HAS_SIGNAL) {
61834ce475dSJemma Denson 		cmd.id = CMD_READ_SNR;
61934ce475dSJemma Denson 		cmd.len = 1;
62034ce475dSJemma Denson 		cmd.arg[0] = 0x00;
62134ce475dSJemma Denson 
62234ce475dSJemma Denson 		ret = cx24120_message_send(state, &cmd);
62334ce475dSJemma Denson 		if (ret != 0) {
62434ce475dSJemma Denson 			err("error reading signal strength\n");
6259fc18f18SJemma Denson 			return;
62634ce475dSJemma Denson 		}
62734ce475dSJemma Denson 
62834ce475dSJemma Denson 		/* raw */
629b0cdf1a1SJemma Denson 		sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6;
630b0cdf1a1SJemma Denson 		sig = sig << 8;
631b0cdf1a1SJemma Denson 		sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L);
632b0cdf1a1SJemma Denson 		dev_dbg(&state->i2c->dev,
6332f3f07fbSJemma Denson 			"signal strength from firmware = 0x%x\n", sig);
63434ce475dSJemma Denson 
63534ce475dSJemma Denson 		/* cooked */
636b0cdf1a1SJemma Denson 		sig = -100 * sig + 94324;
6379fc18f18SJemma Denson 
6389fc18f18SJemma Denson 		c->strength.stat[0].scale = FE_SCALE_RELATIVE;
639b0cdf1a1SJemma Denson 		c->strength.stat[0].uvalue = sig;
6409fc18f18SJemma Denson 	} else {
6419fc18f18SJemma Denson 		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
6429fc18f18SJemma Denson 	}
6439fc18f18SJemma Denson 
6443b5eb504SJemma Denson 	/* CNR */
6453b5eb504SJemma Denson 	if (state->fe_status & FE_HAS_VITERBI) {
6463b5eb504SJemma Denson 		cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8;
6473b5eb504SJemma Denson 		cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L);
6482f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr);
6493b5eb504SJemma Denson 
6503b5eb504SJemma Denson 		/* guessed - seems about right */
6513b5eb504SJemma Denson 		cnr = cnr * 100;
6523b5eb504SJemma Denson 
6533b5eb504SJemma Denson 		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
6543b5eb504SJemma Denson 		c->cnr.stat[0].svalue = cnr;
6553b5eb504SJemma Denson 	} else {
6563b5eb504SJemma Denson 		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
6573b5eb504SJemma Denson 	}
6589fc18f18SJemma Denson 
659e3f2f63eSJemma Denson 	/* BER & UCB require lock */
660e3f2f63eSJemma Denson 	if (!(state->fe_status & FE_HAS_LOCK)) {
661e3f2f63eSJemma Denson 		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
662e3f2f63eSJemma Denson 		c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
663e3f2f63eSJemma Denson 		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
664e3f2f63eSJemma Denson 		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
665e3f2f63eSJemma Denson 		return;
666e3f2f63eSJemma Denson 	}
667e3f2f63eSJemma Denson 
668ddcb252eSJemma Denson 	/* BER */
669ddcb252eSJemma Denson 	if (time_after(jiffies, state->ber_jiffies_stats)) {
670ddcb252eSJemma Denson 		msecs = (state->berw_usecs + 500) / 1000;
671ddcb252eSJemma Denson 		state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs);
672ddcb252eSJemma Denson 
673fc443284SJemma Denson 		ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24;
674fc443284SJemma Denson 		ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16;
675fc443284SJemma Denson 		ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8;
676fc443284SJemma Denson 		ber |= cx24120_readreg(state, CX24120_REG_BER_LL);
677fc443284SJemma Denson 		dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber);
678ddcb252eSJemma Denson 
679ddcb252eSJemma Denson 		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
680ddcb252eSJemma Denson 		c->post_bit_error.stat[0].uvalue += ber;
681ddcb252eSJemma Denson 
682ddcb252eSJemma Denson 		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
683ddcb252eSJemma Denson 		c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE;
684ddcb252eSJemma Denson 	}
685ddcb252eSJemma Denson 
68680e9710bSJemma Denson 	/* UCB */
68780e9710bSJemma Denson 	if (time_after(jiffies, state->per_jiffies_stats)) {
68880e9710bSJemma Denson 		state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
68980e9710bSJemma Denson 
69080e9710bSJemma Denson 		ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8;
69180e9710bSJemma Denson 		ucb |= cx24120_readreg(state, CX24120_REG_UCB_L);
69280e9710bSJemma Denson 		dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb);
69380e9710bSJemma Denson 
694bf8de2d3SJemma Denson 		/* handle reset */
695bf8de2d3SJemma Denson 		if (ucb < state->ucb_offset)
696bf8de2d3SJemma Denson 			state->ucb_offset = c->block_error.stat[0].uvalue;
697bf8de2d3SJemma Denson 
69880e9710bSJemma Denson 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
699bf8de2d3SJemma Denson 		c->block_error.stat[0].uvalue = ucb + state->ucb_offset;
70080e9710bSJemma Denson 
70180e9710bSJemma Denson 		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
70280e9710bSJemma Denson 		c->block_count.stat[0].uvalue += state->bitrate / 8 / 208;
70380e9710bSJemma Denson 	}
7049fc18f18SJemma Denson }
7059fc18f18SJemma Denson 
7066138dc2fSJemma Denson static void cx24120_set_clock_ratios(struct dvb_frontend *fe);
7076138dc2fSJemma Denson 
7085afc9a25SJemma Denson /* Read current tuning status */
cx24120_read_status(struct dvb_frontend * fe,enum fe_status * status)7090df289a2SMauro Carvalho Chehab static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status)
7105afc9a25SJemma Denson {
7115afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
7125afc9a25SJemma Denson 	int lock;
7135afc9a25SJemma Denson 
7145afc9a25SJemma Denson 	lock = cx24120_readreg(state, CX24120_REG_STATUS);
7155afc9a25SJemma Denson 
7162f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock);
7175afc9a25SJemma Denson 
7185afc9a25SJemma Denson 	*status = 0;
7195afc9a25SJemma Denson 
7205afc9a25SJemma Denson 	if (lock & CX24120_HAS_SIGNAL)
7215afc9a25SJemma Denson 		*status = FE_HAS_SIGNAL;
7225afc9a25SJemma Denson 	if (lock & CX24120_HAS_CARRIER)
7235afc9a25SJemma Denson 		*status |= FE_HAS_CARRIER;
7245afc9a25SJemma Denson 	if (lock & CX24120_HAS_VITERBI)
7255afc9a25SJemma Denson 		*status |= FE_HAS_VITERBI | FE_HAS_SYNC;
7265afc9a25SJemma Denson 	if (lock & CX24120_HAS_LOCK)
7275afc9a25SJemma Denson 		*status |= FE_HAS_LOCK;
7285afc9a25SJemma Denson 
729fbdbab72SJemma Denson 	/*
730fbdbab72SJemma Denson 	 * TODO: is FE_HAS_SYNC in the right place?
7315afc9a25SJemma Denson 	 * Other cx241xx drivers have this slightly
732fbdbab72SJemma Denson 	 * different
733fbdbab72SJemma Denson 	 */
7345afc9a25SJemma Denson 
7351462612cSJemma Denson 	state->fe_status = *status;
7361462612cSJemma Denson 	cx24120_get_stats(state);
7379fc18f18SJemma Denson 
7386138dc2fSJemma Denson 	/* Set the clock once tuned in */
7396138dc2fSJemma Denson 	if (state->need_clock_set && *status & FE_HAS_LOCK) {
7406138dc2fSJemma Denson 		/* Set clock ratios */
7416138dc2fSJemma Denson 		cx24120_set_clock_ratios(fe);
7426138dc2fSJemma Denson 
7436138dc2fSJemma Denson 		/* Old driver would do a msleep(200) here */
7446138dc2fSJemma Denson 
7456138dc2fSJemma Denson 		/* Renable mpeg output */
7466138dc2fSJemma Denson 		if (!state->mpeg_enabled)
7476138dc2fSJemma Denson 			cx24120_msg_mpeg_output_global_config(state, 1);
7486138dc2fSJemma Denson 
7496138dc2fSJemma Denson 		state->need_clock_set = 0;
7506138dc2fSJemma Denson 	}
7516138dc2fSJemma Denson 
7525afc9a25SJemma Denson 	return 0;
7535afc9a25SJemma Denson }
7545afc9a25SJemma Denson 
755fbdbab72SJemma Denson /*
756fbdbab72SJemma Denson  * FEC & modulation lookup table
7575afc9a25SJemma Denson  * Used for decoding the REG_FECMODE register
7585afc9a25SJemma Denson  * once tuned in.
7595afc9a25SJemma Denson  */
760ec8fe6c9SMauro Carvalho Chehab struct cx24120_modfec {
7610df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
7620df289a2SMauro Carvalho Chehab 	enum fe_modulation mod;
7630df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
7645afc9a25SJemma Denson 	u8 val;
765ec8fe6c9SMauro Carvalho Chehab };
766ec8fe6c9SMauro Carvalho Chehab 
767ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_modfec modfec_lookup_table[] = {
7685afc9a25SJemma Denson 	/*delsys     mod    fec       val */
7695afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_1_2,  0x01 },
7705afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_2_3,  0x02 },
7715afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_3_4,  0x03 },
7725afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_4_5,  0x04 },
7735afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_5_6,  0x05 },
7745afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_6_7,  0x06 },
7755afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_7_8,  0x07 },
7765afc9a25SJemma Denson 
7775afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_1_2,  0x04 },
7785afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_3_5,  0x05 },
7795afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_2_3,  0x06 },
7805afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_3_4,  0x07 },
7815afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_4_5,  0x08 },
7825afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_5_6,  0x09 },
7835afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_8_9,  0x0a },
7845afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_9_10, 0x0b },
7855afc9a25SJemma Denson 
7865afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_3_5,  0x0c },
7875afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_2_3,  0x0d },
7885afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_3_4,  0x0e },
7895afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_5_6,  0x0f },
7905afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_8_9,  0x10 },
7915afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
7925afc9a25SJemma Denson };
7935afc9a25SJemma Denson 
7945afc9a25SJemma Denson /* Retrieve current fec, modulation & pilot values */
cx24120_get_fec(struct dvb_frontend * fe)7955afc9a25SJemma Denson static int cx24120_get_fec(struct dvb_frontend *fe)
7965afc9a25SJemma Denson {
7975afc9a25SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7985afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
7995afc9a25SJemma Denson 	int idx;
8005afc9a25SJemma Denson 	int ret;
8011ff2e8edSPatrick Boettcher 	int fec;
8025afc9a25SJemma Denson 
8035afc9a25SJemma Denson 	ret = cx24120_readreg(state, CX24120_REG_FECMODE);
8041ff2e8edSPatrick Boettcher 	fec = ret & 0x3f; /* Lower 6 bits */
8055afc9a25SJemma Denson 
8062f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec);
8075afc9a25SJemma Denson 
8085afc9a25SJemma Denson 	for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) {
8095afc9a25SJemma Denson 		if (modfec_lookup_table[idx].delsys != state->dcur.delsys)
8105afc9a25SJemma Denson 			continue;
8111ff2e8edSPatrick Boettcher 		if (modfec_lookup_table[idx].val != fec)
8125afc9a25SJemma Denson 			continue;
8135afc9a25SJemma Denson 
8145afc9a25SJemma Denson 		break; /* found */
8155afc9a25SJemma Denson 	}
8165afc9a25SJemma Denson 
8175afc9a25SJemma Denson 	if (idx >= ARRAY_SIZE(modfec_lookup_table)) {
8182f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "couldn't find fec!\n");
8195afc9a25SJemma Denson 		return -EINVAL;
8205afc9a25SJemma Denson 	}
8215afc9a25SJemma Denson 
8225afc9a25SJemma Denson 	/* save values back to cache */
8235afc9a25SJemma Denson 	c->modulation = modfec_lookup_table[idx].mod;
8245afc9a25SJemma Denson 	c->fec_inner = modfec_lookup_table[idx].fec;
8255afc9a25SJemma Denson 	c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF;
8265afc9a25SJemma Denson 
8272f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n",
8285afc9a25SJemma Denson 		c->modulation, c->fec_inner, c->pilot);
8295afc9a25SJemma Denson 
8305afc9a25SJemma Denson 	return 0;
8315afc9a25SJemma Denson }
8325afc9a25SJemma Denson 
833ddcb252eSJemma Denson /* Calculate ber window time */
cx24120_calculate_ber_window(struct cx24120_state * state,u32 rate)834edff2bacSHans Verkuil static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate)
835ddcb252eSJemma Denson {
836ddcb252eSJemma Denson 	struct dvb_frontend *fe = &state->frontend;
837ddcb252eSJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
83880e9710bSJemma Denson 	u64 tmp;
839ddcb252eSJemma Denson 
840ddcb252eSJemma Denson 	/*
841ddcb252eSJemma Denson 	 * Calculate bitrate from rate in the clock ratios table.
842ddcb252eSJemma Denson 	 * This isn't *exactly* right but close enough.
843ddcb252eSJemma Denson 	 */
84480e9710bSJemma Denson 	tmp = (u64)c->symbol_rate * rate;
84580e9710bSJemma Denson 	do_div(tmp, 256);
84680e9710bSJemma Denson 	state->bitrate = tmp;
847ddcb252eSJemma Denson 
848ddcb252eSJemma Denson 	/* usecs per ber window */
849ddcb252eSJemma Denson 	tmp = 1000000ULL * CX24120_BER_WSIZE;
85080e9710bSJemma Denson 	do_div(tmp, state->bitrate);
851ddcb252eSJemma Denson 	state->berw_usecs = tmp;
852ddcb252eSJemma Denson 
85380e9710bSJemma Denson 	dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n",
85480e9710bSJemma Denson 		state->bitrate, state->berw_usecs);
855ddcb252eSJemma Denson }
856ddcb252eSJemma Denson 
857fbdbab72SJemma Denson /*
858fbdbab72SJemma Denson  * Clock ratios lookup table
8595afc9a25SJemma Denson  *
8605afc9a25SJemma Denson  * Values obtained from much larger table in old driver
8615afc9a25SJemma Denson  * which had numerous entries which would never match.
8625afc9a25SJemma Denson  *
8635afc9a25SJemma Denson  * There's probably some way of calculating these but I
8645afc9a25SJemma Denson  * can't determine the pattern
8655afc9a25SJemma Denson  */
866ec8fe6c9SMauro Carvalho Chehab struct cx24120_clock_ratios_table {
8670df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
8680df289a2SMauro Carvalho Chehab 	enum fe_pilot pilot;
8690df289a2SMauro Carvalho Chehab 	enum fe_modulation mod;
8700df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
8715afc9a25SJemma Denson 	u32 m_rat;
8725afc9a25SJemma Denson 	u32 n_rat;
8735afc9a25SJemma Denson 	u32 rate;
874ec8fe6c9SMauro Carvalho Chehab };
875ec8fe6c9SMauro Carvalho Chehab 
876ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_clock_ratios_table clock_ratios_table[] = {
8775afc9a25SJemma Denson 	/*delsys     pilot      mod    fec       m_rat    n_rat   rate */
8785afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_1_2,  273088,  254505, 274 },
8795afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_3_5,  17272,   13395,  330 },
8805afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_2_3,  24344,   16967,  367 },
8815afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_3_4,  410788,  254505, 413 },
8825afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_4_5,  438328,  254505, 440 },
8835afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_5_6,  30464,   16967,  459 },
8845afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_8_9,  487832,  254505, 490 },
8855afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, QPSK,  FEC_9_10, 493952,  254505, 496 },
8865afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5,  328168,  169905, 494 },
8875afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3,  24344,   11327,  550 },
8885afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4,  410788,  169905, 618 },
8895afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6,  30464,   11327,  688 },
8905afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9,  487832,  169905, 735 },
8915afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952,  169905, 744 },
8925afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_1_2,  273088,  260709, 268 },
8935afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_3_5,  328168,  260709, 322 },
8945afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_2_3,  121720,  86903,  358 },
8955afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_3_4,  410788,  260709, 403 },
8965afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_4_5,  438328,  260709, 430 },
8975afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_5_6,  152320,  86903,  448 },
8985afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_8_9,  487832,  260709, 479 },
8995afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  QPSK,  FEC_9_10, 493952,  260709, 485 },
9005afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_3_5,  328168,  173853, 483 },
9015afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_2_3,  121720,  57951,  537 },
9025afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_3_4,  410788,  173853, 604 },
9035afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_5_6,  152320,  57951,  672 },
9045afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_8_9,  487832,  173853, 718 },
9055afc9a25SJemma Denson 	{ SYS_DVBS2, PILOT_ON,  PSK_8, FEC_9_10, 493952,  173853, 727 },
9065afc9a25SJemma Denson 	{ SYS_DVBS,  PILOT_OFF, QPSK,  FEC_1_2,  152592,  152592, 256 },
9075afc9a25SJemma Denson 	{ SYS_DVBS,  PILOT_OFF, QPSK,  FEC_2_3,  305184,  228888, 341 },
9085afc9a25SJemma Denson 	{ SYS_DVBS,  PILOT_OFF, QPSK,  FEC_3_4,  457776,  305184, 384 },
9095afc9a25SJemma Denson 	{ SYS_DVBS,  PILOT_OFF, QPSK,  FEC_5_6,  762960,  457776, 427 },
9105afc9a25SJemma Denson 	{ SYS_DVBS,  PILOT_OFF, QPSK,  FEC_7_8,  1068144, 610368, 448 },
9115afc9a25SJemma Denson };
9125afc9a25SJemma Denson 
9135afc9a25SJemma Denson /* Set clock ratio from lookup table */
cx24120_set_clock_ratios(struct dvb_frontend * fe)9145afc9a25SJemma Denson static void cx24120_set_clock_ratios(struct dvb_frontend *fe)
9155afc9a25SJemma Denson {
9165afc9a25SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
9175afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
9185afc9a25SJemma Denson 	struct cx24120_cmd cmd;
9195afc9a25SJemma Denson 	int ret, idx;
9205afc9a25SJemma Denson 
9215afc9a25SJemma Denson 	/* Find fec, modulation, pilot */
9225afc9a25SJemma Denson 	ret = cx24120_get_fec(fe);
9235afc9a25SJemma Denson 	if (ret != 0)
9245afc9a25SJemma Denson 		return;
9255afc9a25SJemma Denson 
9265afc9a25SJemma Denson 	/* Find the clock ratios in the lookup table */
9275afc9a25SJemma Denson 	for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) {
9285afc9a25SJemma Denson 		if (clock_ratios_table[idx].delsys != state->dcur.delsys)
9295afc9a25SJemma Denson 			continue;
9305afc9a25SJemma Denson 		if (clock_ratios_table[idx].mod != c->modulation)
9315afc9a25SJemma Denson 			continue;
9325afc9a25SJemma Denson 		if (clock_ratios_table[idx].fec != c->fec_inner)
9335afc9a25SJemma Denson 			continue;
9345afc9a25SJemma Denson 		if (clock_ratios_table[idx].pilot != c->pilot)
9355afc9a25SJemma Denson 			continue;
9365afc9a25SJemma Denson 
9375afc9a25SJemma Denson 		break;		/* found */
9385afc9a25SJemma Denson 	}
9395afc9a25SJemma Denson 
9405afc9a25SJemma Denson 	if (idx >= ARRAY_SIZE(clock_ratios_table)) {
9415afc9a25SJemma Denson 		info("Clock ratio not found - data reception in danger\n");
9425afc9a25SJemma Denson 		return;
9435afc9a25SJemma Denson 	}
9445afc9a25SJemma Denson 
9455afc9a25SJemma Denson 	/* Read current values? */
9465afc9a25SJemma Denson 	cmd.id = CMD_CLOCK_READ;
9475afc9a25SJemma Denson 	cmd.len = 1;
9485afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
9495afc9a25SJemma Denson 	ret = cx24120_message_sendrcv(state, &cmd, 6);
9505afc9a25SJemma Denson 	if (ret != 0)
9515afc9a25SJemma Denson 		return;
9525afc9a25SJemma Denson 	/* in cmd[0]-[5] - result */
9535afc9a25SJemma Denson 
9542f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n",
9555afc9a25SJemma Denson 		cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16),
9565afc9a25SJemma Denson 		cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16),
9575afc9a25SJemma Denson 		idx,
9585afc9a25SJemma Denson 		clock_ratios_table[idx].m_rat,
9595afc9a25SJemma Denson 		clock_ratios_table[idx].n_rat,
9605afc9a25SJemma Denson 		clock_ratios_table[idx].rate);
9615afc9a25SJemma Denson 
9625afc9a25SJemma Denson 	/* Set the clock */
9635afc9a25SJemma Denson 	cmd.id = CMD_CLOCK_SET;
9645afc9a25SJemma Denson 	cmd.len = 10;
9655afc9a25SJemma Denson 	cmd.arg[0] = 0;
9665afc9a25SJemma Denson 	cmd.arg[1] = 0x10;
9675afc9a25SJemma Denson 	cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff;
9685afc9a25SJemma Denson 	cmd.arg[3] = (clock_ratios_table[idx].m_rat >>  8) & 0xff;
9695afc9a25SJemma Denson 	cmd.arg[4] = (clock_ratios_table[idx].m_rat >>  0) & 0xff;
9705afc9a25SJemma Denson 	cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff;
9715afc9a25SJemma Denson 	cmd.arg[6] = (clock_ratios_table[idx].n_rat >>  8) & 0xff;
9725afc9a25SJemma Denson 	cmd.arg[7] = (clock_ratios_table[idx].n_rat >>  0) & 0xff;
9735afc9a25SJemma Denson 	cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff;
9745afc9a25SJemma Denson 	cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff;
9755afc9a25SJemma Denson 
97696002c0aSDaniil Dulov 	ret = cx24120_message_send(state, &cmd);
97796002c0aSDaniil Dulov 	if (ret != 0)
97896002c0aSDaniil Dulov 		return;
979ddcb252eSJemma Denson 
980ddcb252eSJemma Denson 	/* Calculate ber window rates for stat work */
981ddcb252eSJemma Denson 	cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate);
9825afc9a25SJemma Denson }
9835afc9a25SJemma Denson 
9845afc9a25SJemma Denson /* Set inversion value */
cx24120_set_inversion(struct cx24120_state * state,enum fe_spectral_inversion inversion)9855afc9a25SJemma Denson static int cx24120_set_inversion(struct cx24120_state *state,
9860df289a2SMauro Carvalho Chehab 				 enum fe_spectral_inversion inversion)
9875afc9a25SJemma Denson {
9882f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", inversion);
9895afc9a25SJemma Denson 
9905afc9a25SJemma Denson 	switch (inversion) {
9915afc9a25SJemma Denson 	case INVERSION_OFF:
9925afc9a25SJemma Denson 		state->dnxt.inversion_val = 0x00;
9935afc9a25SJemma Denson 		break;
9945afc9a25SJemma Denson 	case INVERSION_ON:
9955afc9a25SJemma Denson 		state->dnxt.inversion_val = 0x04;
9965afc9a25SJemma Denson 		break;
9975afc9a25SJemma Denson 	case INVERSION_AUTO:
9985afc9a25SJemma Denson 		state->dnxt.inversion_val = 0x0c;
9995afc9a25SJemma Denson 		break;
10005afc9a25SJemma Denson 	default:
10015afc9a25SJemma Denson 		return -EINVAL;
10025afc9a25SJemma Denson 	}
10035afc9a25SJemma Denson 
10045afc9a25SJemma Denson 	state->dnxt.inversion = inversion;
10055afc9a25SJemma Denson 
10065afc9a25SJemma Denson 	return 0;
10075afc9a25SJemma Denson }
10085afc9a25SJemma Denson 
10095c0a1c28SJemma Denson /* FEC lookup table for tuning */
1010ec8fe6c9SMauro Carvalho Chehab struct cx24120_modfec_table {
10110df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
10120df289a2SMauro Carvalho Chehab 	enum fe_modulation mod;
10130df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
10145afc9a25SJemma Denson 	u8 val;
1015ec8fe6c9SMauro Carvalho Chehab };
1016ec8fe6c9SMauro Carvalho Chehab 
1017ec8fe6c9SMauro Carvalho Chehab static const struct cx24120_modfec_table modfec_table[] = {
10185afc9a25SJemma Denson 	/*delsys     mod    fec       val */
10195afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_1_2,  0x2e },
10205afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_2_3,  0x2f },
10215afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_3_4,  0x30 },
10225afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_5_6,  0x31 },
10235afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_6_7,  0x32 },
10245afc9a25SJemma Denson 	{ SYS_DVBS,  QPSK,  FEC_7_8,  0x33 },
10255afc9a25SJemma Denson 
10265c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_1_2,  0x04 },
10275c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_3_5,  0x05 },
10285c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_2_3,  0x06 },
10295afc9a25SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_3_4,  0x07 },
10305c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_4_5,  0x08 },
10315c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_5_6,  0x09 },
10325c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_8_9,  0x0a },
10335c0a1c28SJemma Denson 	{ SYS_DVBS2, QPSK,  FEC_9_10, 0x0b },
10345afc9a25SJemma Denson 
10355c0a1c28SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_3_5,  0x0c },
10365afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_2_3,  0x0d },
10375afc9a25SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_3_4,  0x0e },
10385c0a1c28SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_5_6,  0x0f },
10395c0a1c28SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_8_9,  0x10 },
10405c0a1c28SJemma Denson 	{ SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
10415afc9a25SJemma Denson };
10425afc9a25SJemma Denson 
10435afc9a25SJemma Denson /* Set fec_val & fec_mask values from delsys, modulation & fec */
cx24120_set_fec(struct cx24120_state * state,enum fe_modulation mod,enum fe_code_rate fec)10440df289a2SMauro Carvalho Chehab static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod,
10450df289a2SMauro Carvalho Chehab 			   enum fe_code_rate fec)
10465afc9a25SJemma Denson {
10475afc9a25SJemma Denson 	int idx;
10485afc9a25SJemma Denson 
10492f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec);
10505afc9a25SJemma Denson 
10515afc9a25SJemma Denson 	state->dnxt.fec = fec;
10525afc9a25SJemma Denson 
10535afc9a25SJemma Denson 	/* Lookup fec_val from modfec table */
10545afc9a25SJemma Denson 	for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) {
10555afc9a25SJemma Denson 		if (modfec_table[idx].delsys != state->dnxt.delsys)
10565afc9a25SJemma Denson 			continue;
10575afc9a25SJemma Denson 		if (modfec_table[idx].mod != mod)
10585afc9a25SJemma Denson 			continue;
10595afc9a25SJemma Denson 		if (modfec_table[idx].fec != fec)
10605afc9a25SJemma Denson 			continue;
10615afc9a25SJemma Denson 
10625afc9a25SJemma Denson 		/* found */
10635afc9a25SJemma Denson 		state->dnxt.fec_mask = 0x00;
10645afc9a25SJemma Denson 		state->dnxt.fec_val = modfec_table[idx].val;
10655afc9a25SJemma Denson 		return 0;
10665afc9a25SJemma Denson 	}
10675afc9a25SJemma Denson 
10685afc9a25SJemma Denson 	if (state->dnxt.delsys == SYS_DVBS2) {
10695afc9a25SJemma Denson 		/* DVBS2 auto is 0x00/0x00 */
10705afc9a25SJemma Denson 		state->dnxt.fec_mask = 0x00;
10715afc9a25SJemma Denson 		state->dnxt.fec_val  = 0x00;
10725afc9a25SJemma Denson 	} else {
10735afc9a25SJemma Denson 		/* Set DVB-S to auto */
10745afc9a25SJemma Denson 		state->dnxt.fec_val  = 0x2e;
10755afc9a25SJemma Denson 		state->dnxt.fec_mask = 0xac;
10765afc9a25SJemma Denson 	}
10775afc9a25SJemma Denson 
10785afc9a25SJemma Denson 	return 0;
10795afc9a25SJemma Denson }
10805afc9a25SJemma Denson 
10815afc9a25SJemma Denson /* Set pilot */
cx24120_set_pilot(struct cx24120_state * state,enum fe_pilot pilot)10820df289a2SMauro Carvalho Chehab static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot)
10831ff2e8edSPatrick Boettcher {
10842f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", pilot);
10855afc9a25SJemma Denson 
10865afc9a25SJemma Denson 	/* Pilot only valid in DVBS2 */
10875afc9a25SJemma Denson 	if (state->dnxt.delsys != SYS_DVBS2) {
10885afc9a25SJemma Denson 		state->dnxt.pilot_val = CX24120_PILOT_OFF;
10895afc9a25SJemma Denson 		return 0;
10905afc9a25SJemma Denson 	}
10915afc9a25SJemma Denson 
10925afc9a25SJemma Denson 	switch (pilot) {
10935afc9a25SJemma Denson 	case PILOT_OFF:
10945afc9a25SJemma Denson 		state->dnxt.pilot_val = CX24120_PILOT_OFF;
10955afc9a25SJemma Denson 		break;
10965afc9a25SJemma Denson 	case PILOT_ON:
10975afc9a25SJemma Denson 		state->dnxt.pilot_val = CX24120_PILOT_ON;
10985afc9a25SJemma Denson 		break;
10995afc9a25SJemma Denson 	case PILOT_AUTO:
11005afc9a25SJemma Denson 	default:
11015afc9a25SJemma Denson 		state->dnxt.pilot_val = CX24120_PILOT_AUTO;
11025afc9a25SJemma Denson 	}
11035afc9a25SJemma Denson 
11045afc9a25SJemma Denson 	return 0;
11055afc9a25SJemma Denson }
11065afc9a25SJemma Denson 
11075afc9a25SJemma Denson /* Set symbol rate */
cx24120_set_symbolrate(struct cx24120_state * state,u32 rate)11085afc9a25SJemma Denson static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate)
11095afc9a25SJemma Denson {
11102f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", rate);
11115afc9a25SJemma Denson 
11125afc9a25SJemma Denson 	state->dnxt.symbol_rate = rate;
11135afc9a25SJemma Denson 
11145afc9a25SJemma Denson 	/* Check symbol rate */
11155afc9a25SJemma Denson 	if (rate  > 31000000) {
11165afc9a25SJemma Denson 		state->dnxt.clkdiv  = (-(rate < 31000001) & 3) + 2;
11175afc9a25SJemma Denson 		state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4;
11185afc9a25SJemma Denson 	} else {
11195afc9a25SJemma Denson 		state->dnxt.clkdiv  = 3;
11205afc9a25SJemma Denson 		state->dnxt.ratediv = 6;
11215afc9a25SJemma Denson 	}
11225afc9a25SJemma Denson 
11235afc9a25SJemma Denson 	return 0;
11245afc9a25SJemma Denson }
11255afc9a25SJemma Denson 
11265afc9a25SJemma Denson /* Overwrite the current tuning params, we are about to tune */
cx24120_clone_params(struct dvb_frontend * fe)11275afc9a25SJemma Denson static void cx24120_clone_params(struct dvb_frontend *fe)
11285afc9a25SJemma Denson {
11295afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
11305afc9a25SJemma Denson 
11315afc9a25SJemma Denson 	state->dcur = state->dnxt;
11325afc9a25SJemma Denson }
11335afc9a25SJemma Denson 
cx24120_set_frontend(struct dvb_frontend * fe)11345afc9a25SJemma Denson static int cx24120_set_frontend(struct dvb_frontend *fe)
11355afc9a25SJemma Denson {
11365afc9a25SJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
11375afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
11385afc9a25SJemma Denson 	struct cx24120_cmd cmd;
11395afc9a25SJemma Denson 	int ret;
11405afc9a25SJemma Denson 
11415afc9a25SJemma Denson 	switch (c->delivery_system) {
11425afc9a25SJemma Denson 	case SYS_DVBS2:
11432f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "DVB-S2\n");
11445afc9a25SJemma Denson 		break;
11455afc9a25SJemma Denson 	case SYS_DVBS:
11462f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "DVB-S\n");
11475afc9a25SJemma Denson 		break;
11485afc9a25SJemma Denson 	default:
11495afc9a25SJemma Denson 		dev_dbg(&state->i2c->dev,
11502f3f07fbSJemma Denson 			"delivery system(%d) not supported\n",
11512f3f07fbSJemma Denson 			c->delivery_system);
1152c0b34ab1SColin Ian King 		return -EINVAL;
11535afc9a25SJemma Denson 	}
11545afc9a25SJemma Denson 
11555afc9a25SJemma Denson 	state->dnxt.delsys = c->delivery_system;
11565afc9a25SJemma Denson 	state->dnxt.modulation = c->modulation;
11575afc9a25SJemma Denson 	state->dnxt.frequency = c->frequency;
11585afc9a25SJemma Denson 	state->dnxt.pilot = c->pilot;
11595afc9a25SJemma Denson 
11605afc9a25SJemma Denson 	ret = cx24120_set_inversion(state, c->inversion);
11615afc9a25SJemma Denson 	if (ret !=  0)
11625afc9a25SJemma Denson 		return ret;
11635afc9a25SJemma Denson 
11645afc9a25SJemma Denson 	ret = cx24120_set_fec(state, c->modulation, c->fec_inner);
11655afc9a25SJemma Denson 	if (ret !=  0)
11665afc9a25SJemma Denson 		return ret;
11675afc9a25SJemma Denson 
11685afc9a25SJemma Denson 	ret = cx24120_set_pilot(state, c->pilot);
11695afc9a25SJemma Denson 	if (ret != 0)
11705afc9a25SJemma Denson 		return ret;
11715afc9a25SJemma Denson 
11725afc9a25SJemma Denson 	ret = cx24120_set_symbolrate(state, c->symbol_rate);
11735afc9a25SJemma Denson 	if (ret !=  0)
11745afc9a25SJemma Denson 		return ret;
11755afc9a25SJemma Denson 
11765afc9a25SJemma Denson 	/* discard the 'current' tuning parameters and prepare to tune */
11775afc9a25SJemma Denson 	cx24120_clone_params(fe);
11785afc9a25SJemma Denson 
11795afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11802f3f07fbSJemma Denson 		"delsys      = %d\n", state->dcur.delsys);
11815afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11822f3f07fbSJemma Denson 		"modulation  = %d\n", state->dcur.modulation);
11835afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11842f3f07fbSJemma Denson 		"frequency   = %d\n", state->dcur.frequency);
11855afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11862f3f07fbSJemma Denson 		"pilot       = %d (val = 0x%02x)\n",
11875afc9a25SJemma Denson 		state->dcur.pilot, state->dcur.pilot_val);
11885afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11892f3f07fbSJemma Denson 		"symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n",
11902f3f07fbSJemma Denson 		 state->dcur.symbol_rate,
11915afc9a25SJemma Denson 		 state->dcur.clkdiv, state->dcur.ratediv);
11925afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11932f3f07fbSJemma Denson 		"FEC         = %d (mask/val = 0x%02x/0x%02x)\n",
11945afc9a25SJemma Denson 		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
11955afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
11962f3f07fbSJemma Denson 		"Inversion   = %d (val = 0x%02x)\n",
11975afc9a25SJemma Denson 		state->dcur.inversion, state->dcur.inversion_val);
11985afc9a25SJemma Denson 
11996138dc2fSJemma Denson 	/* Flag that clock needs to be set after tune */
12006138dc2fSJemma Denson 	state->need_clock_set = 1;
12016138dc2fSJemma Denson 
12025afc9a25SJemma Denson 	/* Tune in */
12035afc9a25SJemma Denson 	cmd.id = CMD_TUNEREQUEST;
12045afc9a25SJemma Denson 	cmd.len = 15;
12055afc9a25SJemma Denson 	cmd.arg[0] = 0;
12065afc9a25SJemma Denson 	cmd.arg[1]  = (state->dcur.frequency & 0xff0000) >> 16;
12075afc9a25SJemma Denson 	cmd.arg[2]  = (state->dcur.frequency & 0x00ff00) >> 8;
12085afc9a25SJemma Denson 	cmd.arg[3]  = (state->dcur.frequency & 0x0000ff);
12095afc9a25SJemma Denson 	cmd.arg[4]  = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
12105afc9a25SJemma Denson 	cmd.arg[5]  = ((state->dcur.symbol_rate / 1000) & 0x00ff);
12115afc9a25SJemma Denson 	cmd.arg[6]  = state->dcur.inversion;
12125afc9a25SJemma Denson 	cmd.arg[7]  = state->dcur.fec_val | state->dcur.pilot_val;
12135afc9a25SJemma Denson 	cmd.arg[8]  = CX24120_SEARCH_RANGE_KHZ >> 8;
12145afc9a25SJemma Denson 	cmd.arg[9]  = CX24120_SEARCH_RANGE_KHZ & 0xff;
12155afc9a25SJemma Denson 	cmd.arg[10] = 0;		/* maybe rolloff? */
12165afc9a25SJemma Denson 	cmd.arg[11] = state->dcur.fec_mask;
12175afc9a25SJemma Denson 	cmd.arg[12] = state->dcur.ratediv;
12185afc9a25SJemma Denson 	cmd.arg[13] = state->dcur.clkdiv;
12195afc9a25SJemma Denson 	cmd.arg[14] = 0;
12205afc9a25SJemma Denson 
12215afc9a25SJemma Denson 	/* Send tune command */
12225afc9a25SJemma Denson 	ret = cx24120_message_send(state, &cmd);
12235afc9a25SJemma Denson 	if (ret != 0)
12245afc9a25SJemma Denson 		return ret;
12255afc9a25SJemma Denson 
12265afc9a25SJemma Denson 	/* Write symbol rate values */
12275afc9a25SJemma Denson 	ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv);
12285afc9a25SJemma Denson 	ret = cx24120_readreg(state, CX24120_REG_RATEDIV);
12295afc9a25SJemma Denson 	ret &= 0xfffffff0;
12305afc9a25SJemma Denson 	ret |= state->dcur.ratediv;
12315afc9a25SJemma Denson 	ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret);
12325afc9a25SJemma Denson 
12335afc9a25SJemma Denson 	return 0;
12345afc9a25SJemma Denson }
12355afc9a25SJemma Denson 
1236c84251bbSJemma Denson /* Set vco from config */
cx24120_set_vco(struct cx24120_state * state)1237c84251bbSJemma Denson static int cx24120_set_vco(struct cx24120_state *state)
12385afc9a25SJemma Denson {
1239c84251bbSJemma Denson 	struct cx24120_cmd cmd;
1240c84251bbSJemma Denson 	u32 nxtal_khz, vco;
1241c84251bbSJemma Denson 	u64 inv_vco;
12425afc9a25SJemma Denson 	u32 xtal_khz = state->config->xtal_khz;
12435afc9a25SJemma Denson 
1244c84251bbSJemma Denson 	nxtal_khz = xtal_khz * 4;
1245c84251bbSJemma Denson 	vco = nxtal_khz * 10;
1246c84251bbSJemma Denson 	inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco);
12475afc9a25SJemma Denson 
12482f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n",
12492f3f07fbSJemma Denson 		xtal_khz, vco, inv_vco);
12505afc9a25SJemma Denson 
1251c84251bbSJemma Denson 	cmd.id = CMD_VCO_SET;
1252c84251bbSJemma Denson 	cmd.len = 12;
1253c84251bbSJemma Denson 	cmd.arg[0] = (vco >> 16) & 0xff;
1254c84251bbSJemma Denson 	cmd.arg[1] = (vco >> 8) & 0xff;
1255c84251bbSJemma Denson 	cmd.arg[2] = vco & 0xff;
1256c84251bbSJemma Denson 	cmd.arg[3] = (inv_vco >> 8) & 0xff;
1257c84251bbSJemma Denson 	cmd.arg[4] = (inv_vco) & 0xff;
1258c84251bbSJemma Denson 	cmd.arg[5] = 0x03;
1259c84251bbSJemma Denson 	cmd.arg[6] = (nxtal_khz >> 8) & 0xff;
1260c84251bbSJemma Denson 	cmd.arg[7] = nxtal_khz & 0xff;
1261c84251bbSJemma Denson 	cmd.arg[8] = 0x06;
1262c84251bbSJemma Denson 	cmd.arg[9] = 0x03;
1263c84251bbSJemma Denson 	cmd.arg[10] = (xtal_khz >> 16) & 0xff;
1264c84251bbSJemma Denson 	cmd.arg[11] = xtal_khz & 0xff;
1265c84251bbSJemma Denson 
1266c84251bbSJemma Denson 	return cx24120_message_send(state, &cmd);
12675afc9a25SJemma Denson }
12685afc9a25SJemma Denson 
cx24120_init(struct dvb_frontend * fe)12695b8bc802SMauro Carvalho Chehab static int cx24120_init(struct dvb_frontend *fe)
12705afc9a25SJemma Denson {
12715afc9a25SJemma Denson 	const struct firmware *fw;
1272d3cf06bbSJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
12735afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
12745afc9a25SJemma Denson 	struct cx24120_cmd cmd;
12754133601cSJemma Denson 	u8 reg;
12764133601cSJemma Denson 	int ret, i;
12775afc9a25SJemma Denson 	unsigned char vers[4];
12785afc9a25SJemma Denson 
12795afc9a25SJemma Denson 	if (state->cold_init)
12805afc9a25SJemma Denson 		return 0;
12815afc9a25SJemma Denson 
12825afc9a25SJemma Denson 	/* ???? */
128392443cdbSJemma Denson 	cx24120_writereg(state, 0xea, 0x00);
128492443cdbSJemma Denson 	cx24120_test_rom(state);
12851668797dSJemma Denson 	reg = cx24120_readreg(state, 0xfb) & 0xfe;
12861668797dSJemma Denson 	cx24120_writereg(state, 0xfb, reg);
12871668797dSJemma Denson 	reg = cx24120_readreg(state, 0xfc) & 0xfe;
12881668797dSJemma Denson 	cx24120_writereg(state, 0xfc, reg);
128992443cdbSJemma Denson 	cx24120_writereg(state, 0xc3, 0x04);
129092443cdbSJemma Denson 	cx24120_writereg(state, 0xc4, 0x04);
129192443cdbSJemma Denson 	cx24120_writereg(state, 0xce, 0x00);
129292443cdbSJemma Denson 	cx24120_writereg(state, 0xcf, 0x00);
12931668797dSJemma Denson 	reg = cx24120_readreg(state, 0xea) & 0xfe;
12941668797dSJemma Denson 	cx24120_writereg(state, 0xea, reg);
129592443cdbSJemma Denson 	cx24120_writereg(state, 0xeb, 0x0c);
129692443cdbSJemma Denson 	cx24120_writereg(state, 0xec, 0x06);
129792443cdbSJemma Denson 	cx24120_writereg(state, 0xed, 0x05);
129892443cdbSJemma Denson 	cx24120_writereg(state, 0xee, 0x03);
129992443cdbSJemma Denson 	cx24120_writereg(state, 0xef, 0x05);
130092443cdbSJemma Denson 	cx24120_writereg(state, 0xf3, 0x03);
130192443cdbSJemma Denson 	cx24120_writereg(state, 0xf4, 0x44);
13025afc9a25SJemma Denson 
13034133601cSJemma Denson 	for (i = 0; i < 3; i++) {
13044133601cSJemma Denson 		cx24120_writereg(state, 0xf0 + i, 0x04);
13054133601cSJemma Denson 		cx24120_writereg(state, 0xe6 + i, 0x02);
13065afc9a25SJemma Denson 	}
13075afc9a25SJemma Denson 
13081668797dSJemma Denson 	cx24120_writereg(state, 0xea, (reg | 0x01));
13094133601cSJemma Denson 	for (i = 0; i < 6; i += 2) {
13104133601cSJemma Denson 		cx24120_writereg(state, 0xc5 + i, 0x00);
13114133601cSJemma Denson 		cx24120_writereg(state, 0xc6 + i, 0x00);
13125afc9a25SJemma Denson 	}
13135afc9a25SJemma Denson 
131492443cdbSJemma Denson 	cx24120_writereg(state, 0xe4, 0x03);
131592443cdbSJemma Denson 	cx24120_writereg(state, 0xeb, 0x0a);
13165afc9a25SJemma Denson 
13172f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n",
13182f3f07fbSJemma Denson 		CX24120_FIRMWARE);
13195afc9a25SJemma Denson 
13205afc9a25SJemma Denson 	ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE);
13215afc9a25SJemma Denson 	if (ret) {
13221ff2e8edSPatrick Boettcher 		err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE,
13231ff2e8edSPatrick Boettcher 		    ret);
13245afc9a25SJemma Denson 		return ret;
13255afc9a25SJemma Denson 	}
13265afc9a25SJemma Denson 
13275afc9a25SJemma Denson 	dev_dbg(&state->i2c->dev,
13282f3f07fbSJemma Denson 		"Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n",
13295afc9a25SJemma Denson 		(int)fw->size,			/* firmware_size in bytes */
13305afc9a25SJemma Denson 		fw->data[0],			/* fw 1st byte */
13315afc9a25SJemma Denson 		fw->data[1],			/* fw 2d byte */
13325afc9a25SJemma Denson 		fw->data[fw->size - 2],		/* fw before last byte */
13335afc9a25SJemma Denson 		fw->data[fw->size - 1]);	/* fw last byte */
13345afc9a25SJemma Denson 
133592443cdbSJemma Denson 	cx24120_test_rom(state);
13361668797dSJemma Denson 	reg = cx24120_readreg(state, 0xfb) & 0xfe;
13371668797dSJemma Denson 	cx24120_writereg(state, 0xfb, reg);
133892443cdbSJemma Denson 	cx24120_writereg(state, 0xe0, 0x76);
133992443cdbSJemma Denson 	cx24120_writereg(state, 0xf7, 0x81);
134092443cdbSJemma Denson 	cx24120_writereg(state, 0xf8, 0x00);
134192443cdbSJemma Denson 	cx24120_writereg(state, 0xf9, 0x00);
134292443cdbSJemma Denson 	cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00);
134392443cdbSJemma Denson 	cx24120_writereg(state, 0xf7, 0xc0);
134492443cdbSJemma Denson 	cx24120_writereg(state, 0xe0, 0x00);
13451668797dSJemma Denson 	reg = (fw->size - 2) & 0x00ff;
13461668797dSJemma Denson 	cx24120_writereg(state, 0xf8, reg);
13471668797dSJemma Denson 	reg = ((fw->size - 2) >> 8) & 0x00ff;
13481668797dSJemma Denson 	cx24120_writereg(state, 0xf9, reg);
134992443cdbSJemma Denson 	cx24120_writereg(state, 0xf7, 0x00);
135092443cdbSJemma Denson 	cx24120_writereg(state, 0xdc, 0x00);
135192443cdbSJemma Denson 	cx24120_writereg(state, 0xdc, 0x07);
13525afc9a25SJemma Denson 	msleep(500);
13535afc9a25SJemma Denson 
13545afc9a25SJemma Denson 	/* Check final byte matches final byte of firmware */
13551668797dSJemma Denson 	reg = cx24120_readreg(state, 0xe1);
13561668797dSJemma Denson 	if (reg == fw->data[fw->size - 1]) {
13572f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n");
13584133601cSJemma Denson 		ret = 0;
13595afc9a25SJemma Denson 	} else {
13605afc9a25SJemma Denson 		err("Firmware upload failed. Last byte returned=0x%x\n", ret);
13614133601cSJemma Denson 		ret = -EREMOTEIO;
13625afc9a25SJemma Denson 	}
136392443cdbSJemma Denson 	cx24120_writereg(state, 0xdc, 0x00);
13645afc9a25SJemma Denson 	release_firmware(fw);
13654133601cSJemma Denson 	if (ret != 0)
13664133601cSJemma Denson 		return ret;
13675afc9a25SJemma Denson 
13685afc9a25SJemma Denson 	/* Start tuner */
13695afc9a25SJemma Denson 	cmd.id = CMD_START_TUNER;
13705afc9a25SJemma Denson 	cmd.len = 3;
13715afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
13725afc9a25SJemma Denson 	cmd.arg[1] = 0x00;
13735afc9a25SJemma Denson 	cmd.arg[2] = 0x00;
13745afc9a25SJemma Denson 
13755afc9a25SJemma Denson 	if (cx24120_message_send(state, &cmd) != 0) {
13765afc9a25SJemma Denson 		err("Error tuner start! :(\n");
13775afc9a25SJemma Denson 		return -EREMOTEIO;
13785afc9a25SJemma Denson 	}
13795afc9a25SJemma Denson 
13805afc9a25SJemma Denson 	/* Set VCO */
1381c84251bbSJemma Denson 	ret = cx24120_set_vco(state);
1382c84251bbSJemma Denson 	if (ret != 0) {
13835afc9a25SJemma Denson 		err("Error set VCO! :(\n");
1384c84251bbSJemma Denson 		return ret;
13855afc9a25SJemma Denson 	}
13865afc9a25SJemma Denson 
13875afc9a25SJemma Denson 	/* set bandwidth */
13885afc9a25SJemma Denson 	cmd.id = CMD_BANDWIDTH;
13895afc9a25SJemma Denson 	cmd.len = 12;
13905afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
13915afc9a25SJemma Denson 	cmd.arg[1] = 0x00;
13925afc9a25SJemma Denson 	cmd.arg[2] = 0x00;
13935afc9a25SJemma Denson 	cmd.arg[3] = 0x00;
13945afc9a25SJemma Denson 	cmd.arg[4] = 0x05;
13955afc9a25SJemma Denson 	cmd.arg[5] = 0x02;
13965afc9a25SJemma Denson 	cmd.arg[6] = 0x02;
13975afc9a25SJemma Denson 	cmd.arg[7] = 0x00;
13985afc9a25SJemma Denson 	cmd.arg[8] = 0x05;
13995afc9a25SJemma Denson 	cmd.arg[9] = 0x02;
14005afc9a25SJemma Denson 	cmd.arg[10] = 0x02;
14015afc9a25SJemma Denson 	cmd.arg[11] = 0x00;
14025afc9a25SJemma Denson 
14035afc9a25SJemma Denson 	if (cx24120_message_send(state, &cmd)) {
14045afc9a25SJemma Denson 		err("Error set bandwidth!\n");
14055afc9a25SJemma Denson 		return -EREMOTEIO;
14065afc9a25SJemma Denson 	}
14075afc9a25SJemma Denson 
14081668797dSJemma Denson 	reg = cx24120_readreg(state, 0xba);
14091668797dSJemma Denson 	if (reg > 3) {
14102f3f07fbSJemma Denson 		dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret);
14115afc9a25SJemma Denson 		err("Error initialising tuner!\n");
14125afc9a25SJemma Denson 		return -EREMOTEIO;
14135afc9a25SJemma Denson 	}
14145afc9a25SJemma Denson 
14152f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n");
14165afc9a25SJemma Denson 
14175afc9a25SJemma Denson 	/* Initialise mpeg outputs */
141892443cdbSJemma Denson 	cx24120_writereg(state, 0xeb, 0x0a);
14195afc9a25SJemma Denson 	if (cx24120_msg_mpeg_output_global_config(state, 0) ||
14205afc9a25SJemma Denson 	    cx24120_msg_mpeg_output_config(state, 0) ||
14215afc9a25SJemma Denson 	    cx24120_msg_mpeg_output_config(state, 1) ||
14225afc9a25SJemma Denson 	    cx24120_msg_mpeg_output_config(state, 2)) {
14235afc9a25SJemma Denson 		err("Error initialising mpeg output. :(\n");
14245afc9a25SJemma Denson 		return -EREMOTEIO;
14255afc9a25SJemma Denson 	}
14265afc9a25SJemma Denson 
1427ddcb252eSJemma Denson 	/* Set size of BER window */
1428ddcb252eSJemma Denson 	cmd.id = CMD_BER_CTRL;
14295afc9a25SJemma Denson 	cmd.len = 3;
14305afc9a25SJemma Denson 	cmd.arg[0] = 0x00;
1431ddcb252eSJemma Denson 	cmd.arg[1] = CX24120_BER_WINDOW;
1432ddcb252eSJemma Denson 	cmd.arg[2] = CX24120_BER_WINDOW;
14335afc9a25SJemma Denson 	if (cx24120_message_send(state, &cmd)) {
1434ddcb252eSJemma Denson 		err("Error setting ber window\n");
14355afc9a25SJemma Denson 		return -EREMOTEIO;
14365afc9a25SJemma Denson 	}
14375afc9a25SJemma Denson 
14385afc9a25SJemma Denson 	/* Firmware CMD 35: Get firmware version */
14395afc9a25SJemma Denson 	cmd.id = CMD_FWVERSION;
14405afc9a25SJemma Denson 	cmd.len = 1;
14415afc9a25SJemma Denson 	for (i = 0; i < 4; i++) {
14425afc9a25SJemma Denson 		cmd.arg[0] = i;
14435afc9a25SJemma Denson 		ret = cx24120_message_send(state, &cmd);
14445afc9a25SJemma Denson 		if (ret != 0)
14455afc9a25SJemma Denson 			return ret;
14465afc9a25SJemma Denson 		vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX);
14475afc9a25SJemma Denson 	}
14485afc9a25SJemma Denson 	info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]);
14495afc9a25SJemma Denson 
1450d3cf06bbSJemma Denson 	/* init stats here in order signal app which stats are supported */
1451d3cf06bbSJemma Denson 	c->strength.len = 1;
1452d3cf06bbSJemma Denson 	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
14533b5eb504SJemma Denson 	c->cnr.len = 1;
14543b5eb504SJemma Denson 	c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1455ddcb252eSJemma Denson 	c->post_bit_error.len = 1;
1456ddcb252eSJemma Denson 	c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1457ddcb252eSJemma Denson 	c->post_bit_count.len = 1;
1458ddcb252eSJemma Denson 	c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
145980e9710bSJemma Denson 	c->block_error.len = 1;
146080e9710bSJemma Denson 	c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
146180e9710bSJemma Denson 	c->block_count.len = 1;
146280e9710bSJemma Denson 	c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
146380e9710bSJemma Denson 
14645afc9a25SJemma Denson 	state->cold_init = 1;
146571df6731SPatrick Boettcher 
14665afc9a25SJemma Denson 	return 0;
14675afc9a25SJemma Denson }
14685afc9a25SJemma Denson 
cx24120_tune(struct dvb_frontend * fe,bool re_tune,unsigned int mode_flags,unsigned int * delay,enum fe_status * status)14695afc9a25SJemma Denson static int cx24120_tune(struct dvb_frontend *fe, bool re_tune,
14701ff2e8edSPatrick Boettcher 			unsigned int mode_flags, unsigned int *delay,
14710df289a2SMauro Carvalho Chehab 			enum fe_status *status)
14725afc9a25SJemma Denson {
14735afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
14745afc9a25SJemma Denson 	int ret;
14755afc9a25SJemma Denson 
14762f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "(%d)\n", re_tune);
14775afc9a25SJemma Denson 
14785afc9a25SJemma Denson 	/* TODO: Do we need to set delay? */
14795afc9a25SJemma Denson 
14805afc9a25SJemma Denson 	if (re_tune) {
14815afc9a25SJemma Denson 		ret = cx24120_set_frontend(fe);
14825afc9a25SJemma Denson 		if (ret)
14835afc9a25SJemma Denson 			return ret;
14845afc9a25SJemma Denson 	}
14855afc9a25SJemma Denson 
14865afc9a25SJemma Denson 	return cx24120_read_status(fe, status);
14875afc9a25SJemma Denson }
14885afc9a25SJemma Denson 
cx24120_get_algo(struct dvb_frontend * fe)14898d718e53SLuc Van Oostenryck static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe)
14905afc9a25SJemma Denson {
14915afc9a25SJemma Denson 	return DVBFE_ALGO_HW;
14925afc9a25SJemma Denson }
14935afc9a25SJemma Denson 
cx24120_sleep(struct dvb_frontend * fe)14945afc9a25SJemma Denson static int cx24120_sleep(struct dvb_frontend *fe)
14955afc9a25SJemma Denson {
14965afc9a25SJemma Denson 	return 0;
14975afc9a25SJemma Denson }
14985afc9a25SJemma Denson 
cx24120_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)14997e3e68bcSMauro Carvalho Chehab static int cx24120_get_frontend(struct dvb_frontend *fe,
15007e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *c)
15015afc9a25SJemma Denson {
15025afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
15035afc9a25SJemma Denson 	u8 freq1, freq2, freq3;
1504035cad57SJemma Denson 	int status;
15055afc9a25SJemma Denson 
15062f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "\n");
15075afc9a25SJemma Denson 
15085afc9a25SJemma Denson 	/* don't return empty data if we're not tuned in */
1509035cad57SJemma Denson 	status = cx24120_readreg(state, CX24120_REG_STATUS);
1510035cad57SJemma Denson 	if (!(status & CX24120_HAS_LOCK))
15115afc9a25SJemma Denson 		return 0;
15125afc9a25SJemma Denson 
15135afc9a25SJemma Denson 	/* Get frequency */
15145afc9a25SJemma Denson 	freq1 = cx24120_readreg(state, CX24120_REG_FREQ1);
15155afc9a25SJemma Denson 	freq2 = cx24120_readreg(state, CX24120_REG_FREQ2);
15165afc9a25SJemma Denson 	freq3 = cx24120_readreg(state, CX24120_REG_FREQ3);
15175afc9a25SJemma Denson 	c->frequency = (freq3 << 16) | (freq2 << 8) | freq1;
15182f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency);
15195afc9a25SJemma Denson 
15205afc9a25SJemma Denson 	/* Get modulation, fec, pilot */
15215afc9a25SJemma Denson 	cx24120_get_fec(fe);
15225afc9a25SJemma Denson 
15235afc9a25SJemma Denson 	return 0;
15245afc9a25SJemma Denson }
15255afc9a25SJemma Denson 
cx24120_release(struct dvb_frontend * fe)15265afc9a25SJemma Denson static void cx24120_release(struct dvb_frontend *fe)
15275afc9a25SJemma Denson {
15285afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
15295afc9a25SJemma Denson 
15302f3f07fbSJemma Denson 	dev_dbg(&state->i2c->dev, "Clear state structure\n");
15315afc9a25SJemma Denson 	kfree(state);
15325afc9a25SJemma Denson }
15335afc9a25SJemma Denson 
cx24120_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)15345afc9a25SJemma Denson static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
15355afc9a25SJemma Denson {
15365afc9a25SJemma Denson 	struct cx24120_state *state = fe->demodulator_priv;
153780e9710bSJemma Denson 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
15385afc9a25SJemma Denson 
153980e9710bSJemma Denson 	if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) {
154080e9710bSJemma Denson 		*ucblocks = 0;
154180e9710bSJemma Denson 		return 0;
154280e9710bSJemma Denson 	}
15435afc9a25SJemma Denson 
1544bf8de2d3SJemma Denson 	*ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset;
154580e9710bSJemma Denson 
15465afc9a25SJemma Denson 	return 0;
15475afc9a25SJemma Denson }
15485afc9a25SJemma Denson 
1549bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24120_ops = {
15505afc9a25SJemma Denson 	.delsys = { SYS_DVBS, SYS_DVBS2 },
15515afc9a25SJemma Denson 	.info = {
15525afc9a25SJemma Denson 		.name = "Conexant CX24120/CX24118",
1553f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz =  950 * MHz,
1554f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz = 2150 * MHz,
1555f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 1011 * kHz,
1556f1b1eabfSMauro Carvalho Chehab 		.frequency_tolerance_hz = 5 * MHz,
15575afc9a25SJemma Denson 		.symbol_rate_min = 1000000,
15585afc9a25SJemma Denson 		.symbol_rate_max = 45000000,
15595afc9a25SJemma Denson 		.caps =	FE_CAN_INVERSION_AUTO |
15605afc9a25SJemma Denson 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
15615afc9a25SJemma Denson 			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
15625afc9a25SJemma Denson 			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
15635afc9a25SJemma Denson 			FE_CAN_2G_MODULATION |
15645afc9a25SJemma Denson 			FE_CAN_QPSK | FE_CAN_RECOVER
15655afc9a25SJemma Denson 	},
15665afc9a25SJemma Denson 	.release =			cx24120_release,
15675afc9a25SJemma Denson 
15685afc9a25SJemma Denson 	.init =				cx24120_init,
15695afc9a25SJemma Denson 	.sleep =			cx24120_sleep,
15705afc9a25SJemma Denson 
15715afc9a25SJemma Denson 	.tune =				cx24120_tune,
15725afc9a25SJemma Denson 	.get_frontend_algo =		cx24120_get_algo,
15735afc9a25SJemma Denson 	.set_frontend =			cx24120_set_frontend,
15745afc9a25SJemma Denson 
15755afc9a25SJemma Denson 	.get_frontend =			cx24120_get_frontend,
15765afc9a25SJemma Denson 	.read_status =			cx24120_read_status,
15775afc9a25SJemma Denson 	.read_ber =			cx24120_read_ber,
15785afc9a25SJemma Denson 	.read_signal_strength =		cx24120_read_signal_strength,
15795afc9a25SJemma Denson 	.read_snr =			cx24120_read_snr,
15805afc9a25SJemma Denson 	.read_ucblocks =		cx24120_read_ucblocks,
15815afc9a25SJemma Denson 
15825afc9a25SJemma Denson 	.diseqc_send_master_cmd =	cx24120_send_diseqc_msg,
15835afc9a25SJemma Denson 
15845afc9a25SJemma Denson 	.diseqc_send_burst =		cx24120_diseqc_send_burst,
15855afc9a25SJemma Denson 	.set_tone =			cx24120_set_tone,
15865afc9a25SJemma Denson 	.set_voltage =			cx24120_set_voltage,
15875afc9a25SJemma Denson };
15885afc9a25SJemma Denson 
15895afc9a25SJemma Denson MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware");
15905afc9a25SJemma Denson MODULE_AUTHOR("Jemma Denson");
15915afc9a25SJemma Denson MODULE_LICENSE("GPL");
1592