xref: /linux/drivers/media/dvb-frontends/cx24116.c (revision bd336e63441bcdeeccca6a698087d913a32478c5)
19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
59a0bf528SMauro Carvalho Chehab     Copyright (C) 2006-2007 Georg Acher
69a0bf528SMauro Carvalho Chehab     Copyright (C) 2007-2008 Darron Broad
79a0bf528SMauro Carvalho Chehab 	March 2007
89a0bf528SMauro Carvalho Chehab 	    Fixed some bugs.
99a0bf528SMauro Carvalho Chehab 	    Added diseqc support.
109a0bf528SMauro Carvalho Chehab 	    Added corrected signal strength support.
119a0bf528SMauro Carvalho Chehab 	August 2007
129a0bf528SMauro Carvalho Chehab 	    Sync with legacy version.
139a0bf528SMauro Carvalho Chehab 	    Some clean ups.
149a0bf528SMauro Carvalho Chehab     Copyright (C) 2008 Igor Liplianin
159a0bf528SMauro Carvalho Chehab 	September, 9th 2008
169a0bf528SMauro Carvalho Chehab 	    Fixed locking on high symbol rates (>30000).
179a0bf528SMauro Carvalho Chehab 	    Implement MPEG initialization parameter.
189a0bf528SMauro Carvalho Chehab 	January, 17th 2009
199a0bf528SMauro Carvalho Chehab 	    Fill set_voltage with actually control voltage code.
209a0bf528SMauro Carvalho Chehab 	    Correct set tone to not affect voltage.
219a0bf528SMauro Carvalho Chehab 
229a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
239a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
249a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
259a0bf528SMauro Carvalho Chehab     (at your option) any later version.
269a0bf528SMauro Carvalho Chehab 
279a0bf528SMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
289a0bf528SMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
299a0bf528SMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
309a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
319a0bf528SMauro Carvalho Chehab 
329a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
339a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
349a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
359a0bf528SMauro Carvalho Chehab */
369a0bf528SMauro Carvalho Chehab 
379a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
389a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
399a0bf528SMauro Carvalho Chehab #include <linux/module.h>
409a0bf528SMauro Carvalho Chehab #include <linux/moduleparam.h>
419a0bf528SMauro Carvalho Chehab #include <linux/init.h>
429a0bf528SMauro Carvalho Chehab #include <linux/firmware.h>
439a0bf528SMauro Carvalho Chehab 
449a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
459a0bf528SMauro Carvalho Chehab #include "cx24116.h"
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static int debug;
489a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
499a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
529a0bf528SMauro Carvalho Chehab 	do { \
539a0bf528SMauro Carvalho Chehab 		if (debug) \
549a0bf528SMauro Carvalho Chehab 			printk(KERN_INFO "cx24116: " args); \
559a0bf528SMauro Carvalho Chehab 	} while (0)
569a0bf528SMauro Carvalho Chehab 
579a0bf528SMauro Carvalho Chehab #define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
589a0bf528SMauro Carvalho Chehab #define CX24116_SEARCH_RANGE_KHZ 5000
599a0bf528SMauro Carvalho Chehab 
609a0bf528SMauro Carvalho Chehab /* known registers */
619a0bf528SMauro Carvalho Chehab #define CX24116_REG_COMMAND (0x00)      /* command args 0x00..0x1e */
629a0bf528SMauro Carvalho Chehab #define CX24116_REG_EXECUTE (0x1f)      /* execute command */
639a0bf528SMauro Carvalho Chehab #define CX24116_REG_MAILBOX (0x96)      /* FW or multipurpose mailbox? */
649a0bf528SMauro Carvalho Chehab #define CX24116_REG_RESET   (0x20)      /* reset status > 0     */
659a0bf528SMauro Carvalho Chehab #define CX24116_REG_SIGNAL  (0x9e)      /* signal low           */
669a0bf528SMauro Carvalho Chehab #define CX24116_REG_SSTATUS (0x9d)      /* signal high / status */
679a0bf528SMauro Carvalho Chehab #define CX24116_REG_QUALITY8 (0xa3)
689a0bf528SMauro Carvalho Chehab #define CX24116_REG_QSTATUS (0xbc)
699a0bf528SMauro Carvalho Chehab #define CX24116_REG_QUALITY0 (0xd5)
709a0bf528SMauro Carvalho Chehab #define CX24116_REG_BER0    (0xc9)
719a0bf528SMauro Carvalho Chehab #define CX24116_REG_BER8    (0xc8)
729a0bf528SMauro Carvalho Chehab #define CX24116_REG_BER16   (0xc7)
739a0bf528SMauro Carvalho Chehab #define CX24116_REG_BER24   (0xc6)
749a0bf528SMauro Carvalho Chehab #define CX24116_REG_UCB0    (0xcb)
759a0bf528SMauro Carvalho Chehab #define CX24116_REG_UCB8    (0xca)
769a0bf528SMauro Carvalho Chehab #define CX24116_REG_CLKDIV  (0xf3)
779a0bf528SMauro Carvalho Chehab #define CX24116_REG_RATEDIV (0xf9)
789a0bf528SMauro Carvalho Chehab 
799a0bf528SMauro Carvalho Chehab /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
809a0bf528SMauro Carvalho Chehab #define CX24116_REG_FECSTATUS (0x9c)
819a0bf528SMauro Carvalho Chehab 
829a0bf528SMauro Carvalho Chehab /* FECSTATUS bits */
839a0bf528SMauro Carvalho Chehab /* mask to determine configured fec (not tuned) or actual fec (tuned) */
849a0bf528SMauro Carvalho Chehab #define CX24116_FEC_FECMASK   (0x1f)
859a0bf528SMauro Carvalho Chehab 
869a0bf528SMauro Carvalho Chehab /* Select DVB-S demodulator, else DVB-S2 */
879a0bf528SMauro Carvalho Chehab #define CX24116_FEC_DVBS      (0x20)
889a0bf528SMauro Carvalho Chehab #define CX24116_FEC_UNKNOWN   (0x40)    /* Unknown/unused */
899a0bf528SMauro Carvalho Chehab 
909a0bf528SMauro Carvalho Chehab /* Pilot mode requested when tuning else always reset when tuned */
919a0bf528SMauro Carvalho Chehab #define CX24116_FEC_PILOT     (0x80)
929a0bf528SMauro Carvalho Chehab 
939a0bf528SMauro Carvalho Chehab /* arg buffer size */
949a0bf528SMauro Carvalho Chehab #define CX24116_ARGLEN (0x1e)
959a0bf528SMauro Carvalho Chehab 
969a0bf528SMauro Carvalho Chehab /* rolloff */
979a0bf528SMauro Carvalho Chehab #define CX24116_ROLLOFF_020 (0x00)
989a0bf528SMauro Carvalho Chehab #define CX24116_ROLLOFF_025 (0x01)
999a0bf528SMauro Carvalho Chehab #define CX24116_ROLLOFF_035 (0x02)
1009a0bf528SMauro Carvalho Chehab 
1019a0bf528SMauro Carvalho Chehab /* pilot bit */
1029a0bf528SMauro Carvalho Chehab #define CX24116_PILOT_OFF (0x00)
1039a0bf528SMauro Carvalho Chehab #define CX24116_PILOT_ON (0x40)
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab /* signal status */
1069a0bf528SMauro Carvalho Chehab #define CX24116_HAS_SIGNAL   (0x01)
1079a0bf528SMauro Carvalho Chehab #define CX24116_HAS_CARRIER  (0x02)
1089a0bf528SMauro Carvalho Chehab #define CX24116_HAS_VITERBI  (0x04)
1099a0bf528SMauro Carvalho Chehab #define CX24116_HAS_SYNCLOCK (0x08)
1109a0bf528SMauro Carvalho Chehab #define CX24116_HAS_UNKNOWN1 (0x10)
1119a0bf528SMauro Carvalho Chehab #define CX24116_HAS_UNKNOWN2 (0x20)
1129a0bf528SMauro Carvalho Chehab #define CX24116_STATUS_MASK  (0x0f)
1139a0bf528SMauro Carvalho Chehab #define CX24116_SIGNAL_MASK  (0xc0)
1149a0bf528SMauro Carvalho Chehab 
1159a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_TONEOFF   (0)    /* toneburst never sent */
1169a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_TONECACHE (1)    /* toneburst cached     */
1179a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_MESGCACHE (2)    /* message cached       */
1189a0bf528SMauro Carvalho Chehab 
1199a0bf528SMauro Carvalho Chehab /* arg offset for DiSEqC */
1209a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_BURST  (1)
1219a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_ARG2_2 (2)   /* unknown value=2 */
1229a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_ARG3_0 (3)   /* unknown value=0 */
1239a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_ARG4_0 (4)   /* unknown value=0 */
1249a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_MSGLEN (5)
1259a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_MSGOFS (6)
1269a0bf528SMauro Carvalho Chehab 
1279a0bf528SMauro Carvalho Chehab /* DiSEqC burst */
1289a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_MINI_A (0)
1299a0bf528SMauro Carvalho Chehab #define CX24116_DISEQC_MINI_B (1)
1309a0bf528SMauro Carvalho Chehab 
1319a0bf528SMauro Carvalho Chehab /* DiSEqC tone burst */
1329a0bf528SMauro Carvalho Chehab static int toneburst = 1;
1339a0bf528SMauro Carvalho Chehab module_param(toneburst, int, 0644);
1349a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\
1359a0bf528SMauro Carvalho Chehab 	"2=MESSAGE CACHE (default:1)");
1369a0bf528SMauro Carvalho Chehab 
1379a0bf528SMauro Carvalho Chehab /* SNR measurements */
1389a0bf528SMauro Carvalho Chehab static int esno_snr;
1399a0bf528SMauro Carvalho Chehab module_param(esno_snr, int, 0644);
1409a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\
1419a0bf528SMauro Carvalho Chehab 	"1=ESNO(db * 10) (default:0)");
1429a0bf528SMauro Carvalho Chehab 
1439a0bf528SMauro Carvalho Chehab enum cmds {
1449a0bf528SMauro Carvalho Chehab 	CMD_SET_VCO     = 0x10,
1459a0bf528SMauro Carvalho Chehab 	CMD_TUNEREQUEST = 0x11,
1469a0bf528SMauro Carvalho Chehab 	CMD_MPEGCONFIG  = 0x13,
1479a0bf528SMauro Carvalho Chehab 	CMD_TUNERINIT   = 0x14,
1489a0bf528SMauro Carvalho Chehab 	CMD_BANDWIDTH   = 0x15,
1499a0bf528SMauro Carvalho Chehab 	CMD_GETAGC      = 0x19,
1509a0bf528SMauro Carvalho Chehab 	CMD_LNBCONFIG   = 0x20,
1519a0bf528SMauro Carvalho Chehab 	CMD_LNBSEND     = 0x21, /* Formerly CMD_SEND_DISEQC */
1529a0bf528SMauro Carvalho Chehab 	CMD_LNBDCLEVEL  = 0x22,
1539a0bf528SMauro Carvalho Chehab 	CMD_SET_TONE    = 0x23,
1549a0bf528SMauro Carvalho Chehab 	CMD_UPDFWVERS   = 0x35,
1559a0bf528SMauro Carvalho Chehab 	CMD_TUNERSLEEP  = 0x36,
1569a0bf528SMauro Carvalho Chehab 	CMD_AGCCONTROL  = 0x3b, /* Unknown */
1579a0bf528SMauro Carvalho Chehab };
1589a0bf528SMauro Carvalho Chehab 
1599a0bf528SMauro Carvalho Chehab /* The Demod/Tuner can't easily provide these, we cache them */
1609a0bf528SMauro Carvalho Chehab struct cx24116_tuning {
1619a0bf528SMauro Carvalho Chehab 	u32 frequency;
1629a0bf528SMauro Carvalho Chehab 	u32 symbol_rate;
1630df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion;
1640df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
1659a0bf528SMauro Carvalho Chehab 
1660df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delsys;
1670df289a2SMauro Carvalho Chehab 	enum fe_modulation modulation;
1680df289a2SMauro Carvalho Chehab 	enum fe_pilot pilot;
1690df289a2SMauro Carvalho Chehab 	enum fe_rolloff rolloff;
1709a0bf528SMauro Carvalho Chehab 
1719a0bf528SMauro Carvalho Chehab 	/* Demod values */
1729a0bf528SMauro Carvalho Chehab 	u8 fec_val;
1739a0bf528SMauro Carvalho Chehab 	u8 fec_mask;
1749a0bf528SMauro Carvalho Chehab 	u8 inversion_val;
1759a0bf528SMauro Carvalho Chehab 	u8 pilot_val;
1769a0bf528SMauro Carvalho Chehab 	u8 rolloff_val;
1779a0bf528SMauro Carvalho Chehab };
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab /* Basic commands that are sent to the firmware */
1809a0bf528SMauro Carvalho Chehab struct cx24116_cmd {
1819a0bf528SMauro Carvalho Chehab 	u8 len;
1829a0bf528SMauro Carvalho Chehab 	u8 args[CX24116_ARGLEN];
1839a0bf528SMauro Carvalho Chehab };
1849a0bf528SMauro Carvalho Chehab 
1859a0bf528SMauro Carvalho Chehab struct cx24116_state {
1869a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
1879a0bf528SMauro Carvalho Chehab 	const struct cx24116_config *config;
1889a0bf528SMauro Carvalho Chehab 
1899a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
1909a0bf528SMauro Carvalho Chehab 
1919a0bf528SMauro Carvalho Chehab 	struct cx24116_tuning dcur;
1929a0bf528SMauro Carvalho Chehab 	struct cx24116_tuning dnxt;
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 	u8 skip_fw_load;
1959a0bf528SMauro Carvalho Chehab 	u8 burst;
1969a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd dsec_cmd;
1979a0bf528SMauro Carvalho Chehab };
1989a0bf528SMauro Carvalho Chehab 
1999a0bf528SMauro Carvalho Chehab static int cx24116_writereg(struct cx24116_state *state, int reg, int data)
2009a0bf528SMauro Carvalho Chehab {
2019a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
2029a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = state->config->demod_address,
2039a0bf528SMauro Carvalho Chehab 		.flags = 0, .buf = buf, .len = 2 };
2049a0bf528SMauro Carvalho Chehab 	int err;
2059a0bf528SMauro Carvalho Chehab 
2069a0bf528SMauro Carvalho Chehab 	if (debug > 1)
2079a0bf528SMauro Carvalho Chehab 		printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
2089a0bf528SMauro Carvalho Chehab 			__func__, reg, data);
2099a0bf528SMauro Carvalho Chehab 
2109a0bf528SMauro Carvalho Chehab 	err = i2c_transfer(state->i2c, &msg, 1);
2119a0bf528SMauro Carvalho Chehab 	if (err != 1) {
2124bd69e7bSMauro Carvalho Chehab 		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
2134bd69e7bSMauro Carvalho Chehab 		       __func__, err, reg, data);
2149a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
2159a0bf528SMauro Carvalho Chehab 	}
2169a0bf528SMauro Carvalho Chehab 
2179a0bf528SMauro Carvalho Chehab 	return 0;
2189a0bf528SMauro Carvalho Chehab }
2199a0bf528SMauro Carvalho Chehab 
2209a0bf528SMauro Carvalho Chehab /* Bulk byte writes to a single I2C address, for 32k firmware load */
2219a0bf528SMauro Carvalho Chehab static int cx24116_writeregN(struct cx24116_state *state, int reg,
2229a0bf528SMauro Carvalho Chehab 			     const u8 *data, u16 len)
2239a0bf528SMauro Carvalho Chehab {
2249a0bf528SMauro Carvalho Chehab 	int ret = -EREMOTEIO;
2259a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg;
2269a0bf528SMauro Carvalho Chehab 	u8 *buf;
2279a0bf528SMauro Carvalho Chehab 
2289a0bf528SMauro Carvalho Chehab 	buf = kmalloc(len + 1, GFP_KERNEL);
2299a0bf528SMauro Carvalho Chehab 	if (buf == NULL) {
2309a0bf528SMauro Carvalho Chehab 		printk("Unable to kmalloc\n");
2319a0bf528SMauro Carvalho Chehab 		ret = -ENOMEM;
2329a0bf528SMauro Carvalho Chehab 		goto error;
2339a0bf528SMauro Carvalho Chehab 	}
2349a0bf528SMauro Carvalho Chehab 
2359a0bf528SMauro Carvalho Chehab 	*(buf) = reg;
2369a0bf528SMauro Carvalho Chehab 	memcpy(buf + 1, data, len);
2379a0bf528SMauro Carvalho Chehab 
2389a0bf528SMauro Carvalho Chehab 	msg.addr = state->config->demod_address;
2399a0bf528SMauro Carvalho Chehab 	msg.flags = 0;
2409a0bf528SMauro Carvalho Chehab 	msg.buf = buf;
2419a0bf528SMauro Carvalho Chehab 	msg.len = len + 1;
2429a0bf528SMauro Carvalho Chehab 
2439a0bf528SMauro Carvalho Chehab 	if (debug > 1)
2449a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "cx24116: %s:  write regN 0x%02x, len = %d\n",
2459a0bf528SMauro Carvalho Chehab 			__func__, reg, len);
2469a0bf528SMauro Carvalho Chehab 
2479a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
2489a0bf528SMauro Carvalho Chehab 	if (ret != 1) {
2499a0bf528SMauro Carvalho Chehab 		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
2509a0bf528SMauro Carvalho Chehab 			 __func__, ret, reg);
2519a0bf528SMauro Carvalho Chehab 		ret = -EREMOTEIO;
2529a0bf528SMauro Carvalho Chehab 	}
2539a0bf528SMauro Carvalho Chehab 
2549a0bf528SMauro Carvalho Chehab error:
2559a0bf528SMauro Carvalho Chehab 	kfree(buf);
2569a0bf528SMauro Carvalho Chehab 
2579a0bf528SMauro Carvalho Chehab 	return ret;
2589a0bf528SMauro Carvalho Chehab }
2599a0bf528SMauro Carvalho Chehab 
2609a0bf528SMauro Carvalho Chehab static int cx24116_readreg(struct cx24116_state *state, u8 reg)
2619a0bf528SMauro Carvalho Chehab {
2629a0bf528SMauro Carvalho Chehab 	int ret;
2639a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
2649a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
2659a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
2669a0bf528SMauro Carvalho Chehab 		{ .addr = state->config->demod_address, .flags = 0,
2679a0bf528SMauro Carvalho Chehab 			.buf = b0, .len = 1 },
2689a0bf528SMauro Carvalho Chehab 		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
2699a0bf528SMauro Carvalho Chehab 			.buf = b1, .len = 1 }
2709a0bf528SMauro Carvalho Chehab 	};
2719a0bf528SMauro Carvalho Chehab 
2729a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
2739a0bf528SMauro Carvalho Chehab 
2749a0bf528SMauro Carvalho Chehab 	if (ret != 2) {
2759a0bf528SMauro Carvalho Chehab 		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
2769a0bf528SMauro Carvalho Chehab 			__func__, reg, ret);
2779a0bf528SMauro Carvalho Chehab 		return ret;
2789a0bf528SMauro Carvalho Chehab 	}
2799a0bf528SMauro Carvalho Chehab 
2809a0bf528SMauro Carvalho Chehab 	if (debug > 1)
2819a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n",
2829a0bf528SMauro Carvalho Chehab 			reg, b1[0]);
2839a0bf528SMauro Carvalho Chehab 
2849a0bf528SMauro Carvalho Chehab 	return b1[0];
2859a0bf528SMauro Carvalho Chehab }
2869a0bf528SMauro Carvalho Chehab 
2879a0bf528SMauro Carvalho Chehab static int cx24116_set_inversion(struct cx24116_state *state,
2880df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion)
2899a0bf528SMauro Carvalho Chehab {
2909a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, inversion);
2919a0bf528SMauro Carvalho Chehab 
2929a0bf528SMauro Carvalho Chehab 	switch (inversion) {
2939a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
2949a0bf528SMauro Carvalho Chehab 		state->dnxt.inversion_val = 0x00;
2959a0bf528SMauro Carvalho Chehab 		break;
2969a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
2979a0bf528SMauro Carvalho Chehab 		state->dnxt.inversion_val = 0x04;
2989a0bf528SMauro Carvalho Chehab 		break;
2999a0bf528SMauro Carvalho Chehab 	case INVERSION_AUTO:
3009a0bf528SMauro Carvalho Chehab 		state->dnxt.inversion_val = 0x0C;
3019a0bf528SMauro Carvalho Chehab 		break;
3029a0bf528SMauro Carvalho Chehab 	default:
3039a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3049a0bf528SMauro Carvalho Chehab 	}
3059a0bf528SMauro Carvalho Chehab 
3069a0bf528SMauro Carvalho Chehab 	state->dnxt.inversion = inversion;
3079a0bf528SMauro Carvalho Chehab 
3089a0bf528SMauro Carvalho Chehab 	return 0;
3099a0bf528SMauro Carvalho Chehab }
3109a0bf528SMauro Carvalho Chehab 
3119a0bf528SMauro Carvalho Chehab /*
3129a0bf528SMauro Carvalho Chehab  * modfec (modulation and FEC)
3139a0bf528SMauro Carvalho Chehab  * ===========================
3149a0bf528SMauro Carvalho Chehab  *
3159a0bf528SMauro Carvalho Chehab  * MOD          FEC             mask/val    standard
3169a0bf528SMauro Carvalho Chehab  * ----         --------        ----------- --------
3179a0bf528SMauro Carvalho Chehab  * QPSK         FEC_1_2         0x02 0x02+X DVB-S
3189a0bf528SMauro Carvalho Chehab  * QPSK         FEC_2_3         0x04 0x02+X DVB-S
3199a0bf528SMauro Carvalho Chehab  * QPSK         FEC_3_4         0x08 0x02+X DVB-S
3209a0bf528SMauro Carvalho Chehab  * QPSK         FEC_4_5         0x10 0x02+X DVB-S (?)
3219a0bf528SMauro Carvalho Chehab  * QPSK         FEC_5_6         0x20 0x02+X DVB-S
3229a0bf528SMauro Carvalho Chehab  * QPSK         FEC_6_7         0x40 0x02+X DVB-S
3239a0bf528SMauro Carvalho Chehab  * QPSK         FEC_7_8         0x80 0x02+X DVB-S
3249a0bf528SMauro Carvalho Chehab  * QPSK         FEC_8_9         0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
3259a0bf528SMauro Carvalho Chehab  * QPSK         AUTO            0xff 0x02+X DVB-S
3269a0bf528SMauro Carvalho Chehab  *
3279a0bf528SMauro Carvalho Chehab  * For DVB-S high byte probably represents FEC
3289a0bf528SMauro Carvalho Chehab  * and low byte selects the modulator. The high
3299a0bf528SMauro Carvalho Chehab  * byte is search range mask. Bit 5 may turn
3309a0bf528SMauro Carvalho Chehab  * on DVB-S and remaining bits represent some
3319a0bf528SMauro Carvalho Chehab  * kind of calibration (how/what i do not know).
3329a0bf528SMauro Carvalho Chehab  *
3339a0bf528SMauro Carvalho Chehab  * Eg.(2/3) szap "Zone Horror"
3349a0bf528SMauro Carvalho Chehab  *
3359a0bf528SMauro Carvalho Chehab  * mask/val = 0x04, 0x20
3369a0bf528SMauro Carvalho Chehab  * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK
3379a0bf528SMauro Carvalho Chehab  *
3389a0bf528SMauro Carvalho Chehab  * mask/val = 0x04, 0x30
3399a0bf528SMauro Carvalho Chehab  * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK
3409a0bf528SMauro Carvalho Chehab  *
3419a0bf528SMauro Carvalho Chehab  * After tuning FECSTATUS contains actual FEC
3429a0bf528SMauro Carvalho Chehab  * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
3439a0bf528SMauro Carvalho Chehab  *
3449a0bf528SMauro Carvalho Chehab  * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
3459a0bf528SMauro Carvalho Chehab  *
3469a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_1_2         0x00, 0x04      DVB-S2
3479a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_3_5         0x00, 0x05      DVB-S2
3489a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_2_3         0x00, 0x06      DVB-S2
3499a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_3_4         0x00, 0x07      DVB-S2
3509a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_4_5         0x00, 0x08      DVB-S2
3519a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_5_6         0x00, 0x09      DVB-S2
3529a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_8_9         0x00, 0x0a      DVB-S2
3539a0bf528SMauro Carvalho Chehab  * NBC-QPSK     FEC_9_10        0x00, 0x0b      DVB-S2
3549a0bf528SMauro Carvalho Chehab  *
3559a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_3_5         0x00, 0x0c      DVB-S2
3569a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_2_3         0x00, 0x0d      DVB-S2
3579a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_3_4         0x00, 0x0e      DVB-S2
3589a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_5_6         0x00, 0x0f      DVB-S2
3599a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_8_9         0x00, 0x10      DVB-S2
3609a0bf528SMauro Carvalho Chehab  * NBC-8PSK     FEC_9_10        0x00, 0x11      DVB-S2
3619a0bf528SMauro Carvalho Chehab  *
3629a0bf528SMauro Carvalho Chehab  * For DVB-S2 low bytes selects both modulator
3639a0bf528SMauro Carvalho Chehab  * and FEC. High byte is meaningless here. To
3649a0bf528SMauro Carvalho Chehab  * set pilot, bit 6 (0x40) is set. When inspecting
3659a0bf528SMauro Carvalho Chehab  * FECSTATUS bit 7 (0x80) represents the pilot
3669a0bf528SMauro Carvalho Chehab  * selection whilst not tuned. When tuned, actual FEC
3679a0bf528SMauro Carvalho Chehab  * in use is found in FECSTATUS as per above. Pilot
3689a0bf528SMauro Carvalho Chehab  * value is reset.
3699a0bf528SMauro Carvalho Chehab  */
3709a0bf528SMauro Carvalho Chehab 
3719a0bf528SMauro Carvalho Chehab /* A table of modulation, fec and configuration bytes for the demod.
3729a0bf528SMauro Carvalho Chehab  * Not all S2 mmodulation schemes are support and not all rates with
3739a0bf528SMauro Carvalho Chehab  * a scheme are support. Especially, no auto detect when in S2 mode.
3749a0bf528SMauro Carvalho Chehab  */
3759a0bf528SMauro Carvalho Chehab static struct cx24116_modfec {
3760df289a2SMauro Carvalho Chehab 	enum fe_delivery_system delivery_system;
3770df289a2SMauro Carvalho Chehab 	enum fe_modulation modulation;
3780df289a2SMauro Carvalho Chehab 	enum fe_code_rate fec;
3799a0bf528SMauro Carvalho Chehab 	u8 mask;	/* In DVBS mode this is used to autodetect */
3809a0bf528SMauro Carvalho Chehab 	u8 val;		/* Passed to the firmware to indicate mode selection */
3819a0bf528SMauro Carvalho Chehab } CX24116_MODFEC_MODES[] = {
3829a0bf528SMauro Carvalho Chehab  /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab  /*mod   fec       mask  val */
3859a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
3869a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
3879a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
3889a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
3899a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
3909a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
3919a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
3929a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
3939a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
3949a0bf528SMauro Carvalho Chehab  { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
3959a0bf528SMauro Carvalho Chehab  /* NBC-QPSK */
3969a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
3979a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
3989a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
3999a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
4009a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
4019a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
4029a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
4039a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
4049a0bf528SMauro Carvalho Chehab  /* 8PSK */
4059a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
4069a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
4079a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
4089a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
4099a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
4109a0bf528SMauro Carvalho Chehab  { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
4119a0bf528SMauro Carvalho Chehab  /*
4129a0bf528SMauro Carvalho Chehab   * `val' can be found in the FECSTATUS register when tuning.
4139a0bf528SMauro Carvalho Chehab   * FECSTATUS will give the actual FEC in use if tuning was successful.
4149a0bf528SMauro Carvalho Chehab   */
4159a0bf528SMauro Carvalho Chehab };
4169a0bf528SMauro Carvalho Chehab 
4179a0bf528SMauro Carvalho Chehab static int cx24116_lookup_fecmod(struct cx24116_state *state,
4180df289a2SMauro Carvalho Chehab 	enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
4199a0bf528SMauro Carvalho Chehab {
4209a0bf528SMauro Carvalho Chehab 	int i, ret = -EOPNOTSUPP;
4219a0bf528SMauro Carvalho Chehab 
4229a0bf528SMauro Carvalho Chehab 	dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
4239a0bf528SMauro Carvalho Chehab 
4249a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) {
4259a0bf528SMauro Carvalho Chehab 		if ((d == CX24116_MODFEC_MODES[i].delivery_system) &&
4269a0bf528SMauro Carvalho Chehab 			(m == CX24116_MODFEC_MODES[i].modulation) &&
4279a0bf528SMauro Carvalho Chehab 			(f == CX24116_MODFEC_MODES[i].fec)) {
4289a0bf528SMauro Carvalho Chehab 				ret = i;
4299a0bf528SMauro Carvalho Chehab 				break;
4309a0bf528SMauro Carvalho Chehab 			}
4319a0bf528SMauro Carvalho Chehab 	}
4329a0bf528SMauro Carvalho Chehab 
4339a0bf528SMauro Carvalho Chehab 	return ret;
4349a0bf528SMauro Carvalho Chehab }
4359a0bf528SMauro Carvalho Chehab 
4369a0bf528SMauro Carvalho Chehab static int cx24116_set_fec(struct cx24116_state *state,
4370df289a2SMauro Carvalho Chehab 			   enum fe_delivery_system delsys,
4380df289a2SMauro Carvalho Chehab 			   enum fe_modulation mod,
4390df289a2SMauro Carvalho Chehab 			   enum fe_code_rate fec)
4409a0bf528SMauro Carvalho Chehab {
4419a0bf528SMauro Carvalho Chehab 	int ret = 0;
4429a0bf528SMauro Carvalho Chehab 
4439a0bf528SMauro Carvalho Chehab 	dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
4449a0bf528SMauro Carvalho Chehab 
4459a0bf528SMauro Carvalho Chehab 	ret = cx24116_lookup_fecmod(state, delsys, mod, fec);
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 	if (ret < 0)
4489a0bf528SMauro Carvalho Chehab 		return ret;
4499a0bf528SMauro Carvalho Chehab 
4509a0bf528SMauro Carvalho Chehab 	state->dnxt.fec = fec;
4519a0bf528SMauro Carvalho Chehab 	state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
4529a0bf528SMauro Carvalho Chehab 	state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
4539a0bf528SMauro Carvalho Chehab 	dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
4549a0bf528SMauro Carvalho Chehab 		state->dnxt.fec_mask, state->dnxt.fec_val);
4559a0bf528SMauro Carvalho Chehab 
4569a0bf528SMauro Carvalho Chehab 	return 0;
4579a0bf528SMauro Carvalho Chehab }
4589a0bf528SMauro Carvalho Chehab 
4599a0bf528SMauro Carvalho Chehab static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate)
4609a0bf528SMauro Carvalho Chehab {
4619a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, rate);
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 	/*  check if symbol rate is within limits */
4649a0bf528SMauro Carvalho Chehab 	if ((rate > state->frontend.ops.info.symbol_rate_max) ||
4659a0bf528SMauro Carvalho Chehab 	    (rate < state->frontend.ops.info.symbol_rate_min)) {
4669a0bf528SMauro Carvalho Chehab 		dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
4679a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
4689a0bf528SMauro Carvalho Chehab 	}
4699a0bf528SMauro Carvalho Chehab 
4709a0bf528SMauro Carvalho Chehab 	state->dnxt.symbol_rate = rate;
4719a0bf528SMauro Carvalho Chehab 	dprintk("%s() symbol_rate = %d\n", __func__, rate);
4729a0bf528SMauro Carvalho Chehab 
4739a0bf528SMauro Carvalho Chehab 	return 0;
4749a0bf528SMauro Carvalho Chehab }
4759a0bf528SMauro Carvalho Chehab 
4769a0bf528SMauro Carvalho Chehab static int cx24116_load_firmware(struct dvb_frontend *fe,
4779a0bf528SMauro Carvalho Chehab 	const struct firmware *fw);
4789a0bf528SMauro Carvalho Chehab 
4799a0bf528SMauro Carvalho Chehab static int cx24116_firmware_ondemand(struct dvb_frontend *fe)
4809a0bf528SMauro Carvalho Chehab {
4819a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
4829a0bf528SMauro Carvalho Chehab 	const struct firmware *fw;
4839a0bf528SMauro Carvalho Chehab 	int ret = 0;
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
4869a0bf528SMauro Carvalho Chehab 
4879a0bf528SMauro Carvalho Chehab 	if (cx24116_readreg(state, 0x20) > 0) {
4889a0bf528SMauro Carvalho Chehab 
4899a0bf528SMauro Carvalho Chehab 		if (state->skip_fw_load)
4909a0bf528SMauro Carvalho Chehab 			return 0;
4919a0bf528SMauro Carvalho Chehab 
4929a0bf528SMauro Carvalho Chehab 		/* Load firmware */
4939a0bf528SMauro Carvalho Chehab 		/* request the firmware, this will block until loaded */
4949a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n",
4959a0bf528SMauro Carvalho Chehab 			__func__, CX24116_DEFAULT_FIRMWARE);
4969a0bf528SMauro Carvalho Chehab 		ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE,
4979a0bf528SMauro Carvalho Chehab 			state->i2c->dev.parent);
4989a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n",
4999a0bf528SMauro Carvalho Chehab 			__func__);
5009a0bf528SMauro Carvalho Chehab 		if (ret) {
5014bd69e7bSMauro Carvalho Chehab 			printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
5024bd69e7bSMauro Carvalho Chehab 			       __func__);
5039a0bf528SMauro Carvalho Chehab 			return ret;
5049a0bf528SMauro Carvalho Chehab 		}
5059a0bf528SMauro Carvalho Chehab 
5069a0bf528SMauro Carvalho Chehab 		/* Make sure we don't recurse back through here
5079a0bf528SMauro Carvalho Chehab 		 * during loading */
5089a0bf528SMauro Carvalho Chehab 		state->skip_fw_load = 1;
5099a0bf528SMauro Carvalho Chehab 
5109a0bf528SMauro Carvalho Chehab 		ret = cx24116_load_firmware(fe, fw);
5119a0bf528SMauro Carvalho Chehab 		if (ret)
5129a0bf528SMauro Carvalho Chehab 			printk(KERN_ERR "%s: Writing firmware to device failed\n",
5139a0bf528SMauro Carvalho Chehab 				__func__);
5149a0bf528SMauro Carvalho Chehab 
5159a0bf528SMauro Carvalho Chehab 		release_firmware(fw);
5169a0bf528SMauro Carvalho Chehab 
5179a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "%s: Firmware upload %s\n", __func__,
5189a0bf528SMauro Carvalho Chehab 			ret == 0 ? "complete" : "failed");
5199a0bf528SMauro Carvalho Chehab 
5209a0bf528SMauro Carvalho Chehab 		/* Ensure firmware is always loaded if required */
5219a0bf528SMauro Carvalho Chehab 		state->skip_fw_load = 0;
5229a0bf528SMauro Carvalho Chehab 	}
5239a0bf528SMauro Carvalho Chehab 
5249a0bf528SMauro Carvalho Chehab 	return ret;
5259a0bf528SMauro Carvalho Chehab }
5269a0bf528SMauro Carvalho Chehab 
5279a0bf528SMauro Carvalho Chehab /* Take a basic firmware command structure, format it
5289a0bf528SMauro Carvalho Chehab  * and forward it for processing
5299a0bf528SMauro Carvalho Chehab  */
5309a0bf528SMauro Carvalho Chehab static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd)
5319a0bf528SMauro Carvalho Chehab {
5329a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
5339a0bf528SMauro Carvalho Chehab 	int i, ret;
5349a0bf528SMauro Carvalho Chehab 
5359a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
5369a0bf528SMauro Carvalho Chehab 
5379a0bf528SMauro Carvalho Chehab 	/* Load the firmware if required */
5389a0bf528SMauro Carvalho Chehab 	ret = cx24116_firmware_ondemand(fe);
5399a0bf528SMauro Carvalho Chehab 	if (ret != 0) {
5409a0bf528SMauro Carvalho Chehab 		printk(KERN_ERR "%s(): Unable initialise the firmware\n",
5419a0bf528SMauro Carvalho Chehab 			__func__);
5429a0bf528SMauro Carvalho Chehab 		return ret;
5439a0bf528SMauro Carvalho Chehab 	}
5449a0bf528SMauro Carvalho Chehab 
5459a0bf528SMauro Carvalho Chehab 	/* Write the command */
5469a0bf528SMauro Carvalho Chehab 	for (i = 0; i < cmd->len ; i++) {
5479a0bf528SMauro Carvalho Chehab 		dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
5489a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, i, cmd->args[i]);
5499a0bf528SMauro Carvalho Chehab 	}
5509a0bf528SMauro Carvalho Chehab 
5519a0bf528SMauro Carvalho Chehab 	/* Start execution and wait for cmd to terminate */
5529a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
5539a0bf528SMauro Carvalho Chehab 	while (cx24116_readreg(state, CX24116_REG_EXECUTE)) {
5549a0bf528SMauro Carvalho Chehab 		msleep(10);
5559a0bf528SMauro Carvalho Chehab 		if (i++ > 64) {
5569a0bf528SMauro Carvalho Chehab 			/* Avoid looping forever if the firmware does
5579a0bf528SMauro Carvalho Chehab 				not respond */
5589a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "%s() Firmware not responding\n",
5599a0bf528SMauro Carvalho Chehab 				__func__);
5609a0bf528SMauro Carvalho Chehab 			return -EREMOTEIO;
5619a0bf528SMauro Carvalho Chehab 		}
5629a0bf528SMauro Carvalho Chehab 	}
5639a0bf528SMauro Carvalho Chehab 	return 0;
5649a0bf528SMauro Carvalho Chehab }
5659a0bf528SMauro Carvalho Chehab 
5669a0bf528SMauro Carvalho Chehab static int cx24116_load_firmware(struct dvb_frontend *fe,
5679a0bf528SMauro Carvalho Chehab 	const struct firmware *fw)
5689a0bf528SMauro Carvalho Chehab {
5699a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
5709a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
5719a0bf528SMauro Carvalho Chehab 	int i, ret, len, max, remaining;
5729a0bf528SMauro Carvalho Chehab 	unsigned char vers[4];
5739a0bf528SMauro Carvalho Chehab 
5749a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
5759a0bf528SMauro Carvalho Chehab 	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
5769a0bf528SMauro Carvalho Chehab 			fw->size,
5779a0bf528SMauro Carvalho Chehab 			fw->data[0],
5789a0bf528SMauro Carvalho Chehab 			fw->data[1],
5799a0bf528SMauro Carvalho Chehab 			fw->data[fw->size-2],
5809a0bf528SMauro Carvalho Chehab 			fw->data[fw->size-1]);
5819a0bf528SMauro Carvalho Chehab 
5829a0bf528SMauro Carvalho Chehab 	/* Toggle 88x SRST pin to reset demod */
5839a0bf528SMauro Carvalho Chehab 	if (state->config->reset_device)
5849a0bf528SMauro Carvalho Chehab 		state->config->reset_device(fe);
5859a0bf528SMauro Carvalho Chehab 
5869a0bf528SMauro Carvalho Chehab 	/* Begin the firmware load process */
5879a0bf528SMauro Carvalho Chehab 	/* Prepare the demod, load the firmware, cleanup after load */
5889a0bf528SMauro Carvalho Chehab 
5899a0bf528SMauro Carvalho Chehab 	/* Init PLL */
5909a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xE5, 0x00);
5919a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF1, 0x08);
5929a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF2, 0x13);
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab 	/* Start PLL */
5959a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe0, 0x03);
5969a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe0, 0x00);
5979a0bf528SMauro Carvalho Chehab 
5989a0bf528SMauro Carvalho Chehab 	/* Unknown */
5999a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
6009a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
6019a0bf528SMauro Carvalho Chehab 
6029a0bf528SMauro Carvalho Chehab 	/* Unknown */
6039a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF0, 0x03);
6049a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF4, 0x81);
6059a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF5, 0x00);
6069a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF6, 0x00);
6079a0bf528SMauro Carvalho Chehab 
6089a0bf528SMauro Carvalho Chehab 	/* Split firmware to the max I2C write len and write.
6099a0bf528SMauro Carvalho Chehab 	 * Writes whole firmware as one write when i2c_wr_max is set to 0. */
6109a0bf528SMauro Carvalho Chehab 	if (state->config->i2c_wr_max)
6119a0bf528SMauro Carvalho Chehab 		max = state->config->i2c_wr_max;
6129a0bf528SMauro Carvalho Chehab 	else
6139a0bf528SMauro Carvalho Chehab 		max = INT_MAX; /* enough for 32k firmware */
6149a0bf528SMauro Carvalho Chehab 
6159a0bf528SMauro Carvalho Chehab 	for (remaining = fw->size; remaining > 0; remaining -= max - 1) {
6169a0bf528SMauro Carvalho Chehab 		len = remaining;
6179a0bf528SMauro Carvalho Chehab 		if (len > max - 1)
6189a0bf528SMauro Carvalho Chehab 			len = max - 1;
6199a0bf528SMauro Carvalho Chehab 
6209a0bf528SMauro Carvalho Chehab 		cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining],
6219a0bf528SMauro Carvalho Chehab 			len);
6229a0bf528SMauro Carvalho Chehab 	}
6239a0bf528SMauro Carvalho Chehab 
6249a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF4, 0x10);
6259a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF0, 0x00);
6269a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xF8, 0x06);
6279a0bf528SMauro Carvalho Chehab 
6289a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 10: VCO config */
6299a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_SET_VCO;
6309a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x05;
6319a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = 0xdc;
6329a0bf528SMauro Carvalho Chehab 	cmd.args[0x03] = 0xda;
6339a0bf528SMauro Carvalho Chehab 	cmd.args[0x04] = 0xae;
6349a0bf528SMauro Carvalho Chehab 	cmd.args[0x05] = 0xaa;
6359a0bf528SMauro Carvalho Chehab 	cmd.args[0x06] = 0x04;
6369a0bf528SMauro Carvalho Chehab 	cmd.args[0x07] = 0x9d;
6379a0bf528SMauro Carvalho Chehab 	cmd.args[0x08] = 0xfc;
6389a0bf528SMauro Carvalho Chehab 	cmd.args[0x09] = 0x06;
6399a0bf528SMauro Carvalho Chehab 	cmd.len = 0x0a;
6409a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
6419a0bf528SMauro Carvalho Chehab 	if (ret != 0)
6429a0bf528SMauro Carvalho Chehab 		return ret;
6439a0bf528SMauro Carvalho Chehab 
6449a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
6459a0bf528SMauro Carvalho Chehab 
6469a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 14: Tuner config */
6479a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_TUNERINIT;
6489a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x00;
6499a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = 0x00;
6509a0bf528SMauro Carvalho Chehab 	cmd.len = 0x03;
6519a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
6529a0bf528SMauro Carvalho Chehab 	if (ret != 0)
6539a0bf528SMauro Carvalho Chehab 		return ret;
6549a0bf528SMauro Carvalho Chehab 
6559a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe5, 0x00);
6569a0bf528SMauro Carvalho Chehab 
6579a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 13: MPEG config */
6589a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_MPEGCONFIG;
6599a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x01;
6609a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = 0x75;
6619a0bf528SMauro Carvalho Chehab 	cmd.args[0x03] = 0x00;
6629a0bf528SMauro Carvalho Chehab 	if (state->config->mpg_clk_pos_pol)
6639a0bf528SMauro Carvalho Chehab 		cmd.args[0x04] = state->config->mpg_clk_pos_pol;
6649a0bf528SMauro Carvalho Chehab 	else
6659a0bf528SMauro Carvalho Chehab 		cmd.args[0x04] = 0x02;
6669a0bf528SMauro Carvalho Chehab 	cmd.args[0x05] = 0x00;
6679a0bf528SMauro Carvalho Chehab 	cmd.len = 0x06;
6689a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
6699a0bf528SMauro Carvalho Chehab 	if (ret != 0)
6709a0bf528SMauro Carvalho Chehab 		return ret;
6719a0bf528SMauro Carvalho Chehab 
6729a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 35: Get firmware version */
6739a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_UPDFWVERS;
6749a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
6759a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 4; i++) {
6769a0bf528SMauro Carvalho Chehab 		cmd.args[0x01] = i;
6779a0bf528SMauro Carvalho Chehab 		ret = cx24116_cmd_execute(fe, &cmd);
6789a0bf528SMauro Carvalho Chehab 		if (ret != 0)
6799a0bf528SMauro Carvalho Chehab 			return ret;
6809a0bf528SMauro Carvalho Chehab 		vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX);
6819a0bf528SMauro Carvalho Chehab 	}
6829a0bf528SMauro Carvalho Chehab 	printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__,
6839a0bf528SMauro Carvalho Chehab 		vers[0], vers[1], vers[2], vers[3]);
6849a0bf528SMauro Carvalho Chehab 
6859a0bf528SMauro Carvalho Chehab 	return 0;
6869a0bf528SMauro Carvalho Chehab }
6879a0bf528SMauro Carvalho Chehab 
6880df289a2SMauro Carvalho Chehab static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status)
6899a0bf528SMauro Carvalho Chehab {
6909a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
6919a0bf528SMauro Carvalho Chehab 
6929a0bf528SMauro Carvalho Chehab 	int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) &
6939a0bf528SMauro Carvalho Chehab 		CX24116_STATUS_MASK;
6949a0bf528SMauro Carvalho Chehab 
6959a0bf528SMauro Carvalho Chehab 	dprintk("%s: status = 0x%02x\n", __func__, lock);
6969a0bf528SMauro Carvalho Chehab 
6979a0bf528SMauro Carvalho Chehab 	*status = 0;
6989a0bf528SMauro Carvalho Chehab 
6999a0bf528SMauro Carvalho Chehab 	if (lock & CX24116_HAS_SIGNAL)
7009a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
7019a0bf528SMauro Carvalho Chehab 	if (lock & CX24116_HAS_CARRIER)
7029a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
7039a0bf528SMauro Carvalho Chehab 	if (lock & CX24116_HAS_VITERBI)
7049a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
7059a0bf528SMauro Carvalho Chehab 	if (lock & CX24116_HAS_SYNCLOCK)
7069a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
7079a0bf528SMauro Carvalho Chehab 
7089a0bf528SMauro Carvalho Chehab 	return 0;
7099a0bf528SMauro Carvalho Chehab }
7109a0bf528SMauro Carvalho Chehab 
7119a0bf528SMauro Carvalho Chehab static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber)
7129a0bf528SMauro Carvalho Chehab {
7139a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
7149a0bf528SMauro Carvalho Chehab 
7159a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
7169a0bf528SMauro Carvalho Chehab 
7179a0bf528SMauro Carvalho Chehab 	*ber =  (cx24116_readreg(state, CX24116_REG_BER24) << 24) |
7189a0bf528SMauro Carvalho Chehab 		(cx24116_readreg(state, CX24116_REG_BER16) << 16) |
7199a0bf528SMauro Carvalho Chehab 		(cx24116_readreg(state, CX24116_REG_BER8)  << 8)  |
7209a0bf528SMauro Carvalho Chehab 		 cx24116_readreg(state, CX24116_REG_BER0);
7219a0bf528SMauro Carvalho Chehab 
7229a0bf528SMauro Carvalho Chehab 	return 0;
7239a0bf528SMauro Carvalho Chehab }
7249a0bf528SMauro Carvalho Chehab 
7259a0bf528SMauro Carvalho Chehab /* TODO Determine function and scale appropriately */
7269a0bf528SMauro Carvalho Chehab static int cx24116_read_signal_strength(struct dvb_frontend *fe,
7279a0bf528SMauro Carvalho Chehab 	u16 *signal_strength)
7289a0bf528SMauro Carvalho Chehab {
7299a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
7309a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
7319a0bf528SMauro Carvalho Chehab 	int ret;
7329a0bf528SMauro Carvalho Chehab 	u16 sig_reading;
7339a0bf528SMauro Carvalho Chehab 
7349a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
7359a0bf528SMauro Carvalho Chehab 
7369a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 19: Get AGC */
7379a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_GETAGC;
7389a0bf528SMauro Carvalho Chehab 	cmd.len = 0x01;
7399a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
7409a0bf528SMauro Carvalho Chehab 	if (ret != 0)
7419a0bf528SMauro Carvalho Chehab 		return ret;
7429a0bf528SMauro Carvalho Chehab 
7439a0bf528SMauro Carvalho Chehab 	sig_reading =
7449a0bf528SMauro Carvalho Chehab 		(cx24116_readreg(state,
7459a0bf528SMauro Carvalho Chehab 			CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) |
7469a0bf528SMauro Carvalho Chehab 		(cx24116_readreg(state, CX24116_REG_SIGNAL) << 6);
7479a0bf528SMauro Carvalho Chehab 	*signal_strength = 0 - sig_reading;
7489a0bf528SMauro Carvalho Chehab 
7499a0bf528SMauro Carvalho Chehab 	dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n",
7509a0bf528SMauro Carvalho Chehab 		__func__, sig_reading, *signal_strength);
7519a0bf528SMauro Carvalho Chehab 
7529a0bf528SMauro Carvalho Chehab 	return 0;
7539a0bf528SMauro Carvalho Chehab }
7549a0bf528SMauro Carvalho Chehab 
7559a0bf528SMauro Carvalho Chehab /* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
7569a0bf528SMauro Carvalho Chehab static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr)
7579a0bf528SMauro Carvalho Chehab {
7589a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
7599a0bf528SMauro Carvalho Chehab 	u8 snr_reading;
7609a0bf528SMauro Carvalho Chehab 	static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
7619a0bf528SMauro Carvalho Chehab 		0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667,
7629a0bf528SMauro Carvalho Chehab 		0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667,
7639a0bf528SMauro Carvalho Chehab 		0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667,
7649a0bf528SMauro Carvalho Chehab 		0x18000 };
7659a0bf528SMauro Carvalho Chehab 
7669a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
7679a0bf528SMauro Carvalho Chehab 
7689a0bf528SMauro Carvalho Chehab 	snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
7699a0bf528SMauro Carvalho Chehab 
7709a0bf528SMauro Carvalho Chehab 	if (snr_reading >= 0xa0 /* 100% */)
7719a0bf528SMauro Carvalho Chehab 		*snr = 0xffff;
7729a0bf528SMauro Carvalho Chehab 	else
7739a0bf528SMauro Carvalho Chehab 		*snr = snr_tab[(snr_reading & 0xf0) >> 4] +
7749a0bf528SMauro Carvalho Chehab 			(snr_tab[(snr_reading & 0x0f)] >> 4);
7759a0bf528SMauro Carvalho Chehab 
7769a0bf528SMauro Carvalho Chehab 	dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
7779a0bf528SMauro Carvalho Chehab 		snr_reading, *snr);
7789a0bf528SMauro Carvalho Chehab 
7799a0bf528SMauro Carvalho Chehab 	return 0;
7809a0bf528SMauro Carvalho Chehab }
7819a0bf528SMauro Carvalho Chehab 
7829a0bf528SMauro Carvalho Chehab /* The reelbox patches show the value in the registers represents
7839a0bf528SMauro Carvalho Chehab  * ESNO, from 0->30db (values 0->300). We provide this value by
7849a0bf528SMauro Carvalho Chehab  * default.
7859a0bf528SMauro Carvalho Chehab  */
7869a0bf528SMauro Carvalho Chehab static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr)
7879a0bf528SMauro Carvalho Chehab {
7889a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
7899a0bf528SMauro Carvalho Chehab 
7909a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
7919a0bf528SMauro Carvalho Chehab 
7929a0bf528SMauro Carvalho Chehab 	*snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 |
7939a0bf528SMauro Carvalho Chehab 		cx24116_readreg(state, CX24116_REG_QUALITY0);
7949a0bf528SMauro Carvalho Chehab 
7959a0bf528SMauro Carvalho Chehab 	dprintk("%s: raw 0x%04x\n", __func__, *snr);
7969a0bf528SMauro Carvalho Chehab 
7979a0bf528SMauro Carvalho Chehab 	return 0;
7989a0bf528SMauro Carvalho Chehab }
7999a0bf528SMauro Carvalho Chehab 
8009a0bf528SMauro Carvalho Chehab static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr)
8019a0bf528SMauro Carvalho Chehab {
8029a0bf528SMauro Carvalho Chehab 	if (esno_snr == 1)
8039a0bf528SMauro Carvalho Chehab 		return cx24116_read_snr_esno(fe, snr);
8049a0bf528SMauro Carvalho Chehab 	else
8059a0bf528SMauro Carvalho Chehab 		return cx24116_read_snr_pct(fe, snr);
8069a0bf528SMauro Carvalho Chehab }
8079a0bf528SMauro Carvalho Chehab 
8089a0bf528SMauro Carvalho Chehab static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
8099a0bf528SMauro Carvalho Chehab {
8109a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
8119a0bf528SMauro Carvalho Chehab 
8129a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
8139a0bf528SMauro Carvalho Chehab 
8149a0bf528SMauro Carvalho Chehab 	*ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) |
8159a0bf528SMauro Carvalho Chehab 		cx24116_readreg(state, CX24116_REG_UCB0);
8169a0bf528SMauro Carvalho Chehab 
8179a0bf528SMauro Carvalho Chehab 	return 0;
8189a0bf528SMauro Carvalho Chehab }
8199a0bf528SMauro Carvalho Chehab 
8209a0bf528SMauro Carvalho Chehab /* Overwrite the current tuning params, we are about to tune */
8219a0bf528SMauro Carvalho Chehab static void cx24116_clone_params(struct dvb_frontend *fe)
8229a0bf528SMauro Carvalho Chehab {
8239a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
824ee45ddc1SEzequiel Garcia 	state->dcur = state->dnxt;
8259a0bf528SMauro Carvalho Chehab }
8269a0bf528SMauro Carvalho Chehab 
8279a0bf528SMauro Carvalho Chehab /* Wait for LNB */
8289a0bf528SMauro Carvalho Chehab static int cx24116_wait_for_lnb(struct dvb_frontend *fe)
8299a0bf528SMauro Carvalho Chehab {
8309a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
8319a0bf528SMauro Carvalho Chehab 	int i;
8329a0bf528SMauro Carvalho Chehab 
8339a0bf528SMauro Carvalho Chehab 	dprintk("%s() qstatus = 0x%02x\n", __func__,
8349a0bf528SMauro Carvalho Chehab 		cx24116_readreg(state, CX24116_REG_QSTATUS));
8359a0bf528SMauro Carvalho Chehab 
8369a0bf528SMauro Carvalho Chehab 	/* Wait for up to 300 ms */
8379a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 30 ; i++) {
8389a0bf528SMauro Carvalho Chehab 		if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
8399a0bf528SMauro Carvalho Chehab 			return 0;
8409a0bf528SMauro Carvalho Chehab 		msleep(10);
8419a0bf528SMauro Carvalho Chehab 	}
8429a0bf528SMauro Carvalho Chehab 
8439a0bf528SMauro Carvalho Chehab 	dprintk("%s(): LNB not ready\n", __func__);
8449a0bf528SMauro Carvalho Chehab 
8459a0bf528SMauro Carvalho Chehab 	return -ETIMEDOUT; /* -EBUSY ? */
8469a0bf528SMauro Carvalho Chehab }
8479a0bf528SMauro Carvalho Chehab 
8489a0bf528SMauro Carvalho Chehab static int cx24116_set_voltage(struct dvb_frontend *fe,
8490df289a2SMauro Carvalho Chehab 	enum fe_sec_voltage voltage)
8509a0bf528SMauro Carvalho Chehab {
8519a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
8529a0bf528SMauro Carvalho Chehab 	int ret;
8539a0bf528SMauro Carvalho Chehab 
8549a0bf528SMauro Carvalho Chehab 	dprintk("%s: %s\n", __func__,
8559a0bf528SMauro Carvalho Chehab 		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
8569a0bf528SMauro Carvalho Chehab 		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
8579a0bf528SMauro Carvalho Chehab 
8589a0bf528SMauro Carvalho Chehab 	/* Wait for LNB ready */
8599a0bf528SMauro Carvalho Chehab 	ret = cx24116_wait_for_lnb(fe);
8609a0bf528SMauro Carvalho Chehab 	if (ret != 0)
8619a0bf528SMauro Carvalho Chehab 		return ret;
8629a0bf528SMauro Carvalho Chehab 
8639a0bf528SMauro Carvalho Chehab 	/* Wait for voltage/min repeat delay */
8649a0bf528SMauro Carvalho Chehab 	msleep(100);
8659a0bf528SMauro Carvalho Chehab 
8669a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_LNBDCLEVEL;
8679a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
8689a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
8699a0bf528SMauro Carvalho Chehab 
8709a0bf528SMauro Carvalho Chehab 	/* Min delay time before DiSEqC send */
8719a0bf528SMauro Carvalho Chehab 	msleep(15);
8729a0bf528SMauro Carvalho Chehab 
8739a0bf528SMauro Carvalho Chehab 	return cx24116_cmd_execute(fe, &cmd);
8749a0bf528SMauro Carvalho Chehab }
8759a0bf528SMauro Carvalho Chehab 
8769a0bf528SMauro Carvalho Chehab static int cx24116_set_tone(struct dvb_frontend *fe,
8770df289a2SMauro Carvalho Chehab 	enum fe_sec_tone_mode tone)
8789a0bf528SMauro Carvalho Chehab {
8799a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
8809a0bf528SMauro Carvalho Chehab 	int ret;
8819a0bf528SMauro Carvalho Chehab 
8829a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, tone);
8839a0bf528SMauro Carvalho Chehab 	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
8849a0bf528SMauro Carvalho Chehab 		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
8859a0bf528SMauro Carvalho Chehab 		return -EINVAL;
8869a0bf528SMauro Carvalho Chehab 	}
8879a0bf528SMauro Carvalho Chehab 
8889a0bf528SMauro Carvalho Chehab 	/* Wait for LNB ready */
8899a0bf528SMauro Carvalho Chehab 	ret = cx24116_wait_for_lnb(fe);
8909a0bf528SMauro Carvalho Chehab 	if (ret != 0)
8919a0bf528SMauro Carvalho Chehab 		return ret;
8929a0bf528SMauro Carvalho Chehab 
8939a0bf528SMauro Carvalho Chehab 	/* Min delay time after DiSEqC send */
8949a0bf528SMauro Carvalho Chehab 	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
8959a0bf528SMauro Carvalho Chehab 
8969a0bf528SMauro Carvalho Chehab 	/* Now we set the tone */
8979a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_SET_TONE;
8989a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x00;
8999a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = 0x00;
9009a0bf528SMauro Carvalho Chehab 
9019a0bf528SMauro Carvalho Chehab 	switch (tone) {
9029a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
9039a0bf528SMauro Carvalho Chehab 		dprintk("%s: setting tone on\n", __func__);
9049a0bf528SMauro Carvalho Chehab 		cmd.args[0x03] = 0x01;
9059a0bf528SMauro Carvalho Chehab 		break;
9069a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
9079a0bf528SMauro Carvalho Chehab 		dprintk("%s: setting tone off\n", __func__);
9089a0bf528SMauro Carvalho Chehab 		cmd.args[0x03] = 0x00;
9099a0bf528SMauro Carvalho Chehab 		break;
9109a0bf528SMauro Carvalho Chehab 	}
9119a0bf528SMauro Carvalho Chehab 	cmd.len = 0x04;
9129a0bf528SMauro Carvalho Chehab 
9139a0bf528SMauro Carvalho Chehab 	/* Min delay time before DiSEqC send */
9149a0bf528SMauro Carvalho Chehab 	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
9159a0bf528SMauro Carvalho Chehab 
9169a0bf528SMauro Carvalho Chehab 	return cx24116_cmd_execute(fe, &cmd);
9179a0bf528SMauro Carvalho Chehab }
9189a0bf528SMauro Carvalho Chehab 
9199a0bf528SMauro Carvalho Chehab /* Initialise DiSEqC */
9209a0bf528SMauro Carvalho Chehab static int cx24116_diseqc_init(struct dvb_frontend *fe)
9219a0bf528SMauro Carvalho Chehab {
9229a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
9239a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
9249a0bf528SMauro Carvalho Chehab 	int ret;
9259a0bf528SMauro Carvalho Chehab 
9269a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 20: LNB/DiSEqC config */
9279a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_LNBCONFIG;
9289a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x00;
9299a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = 0x10;
9309a0bf528SMauro Carvalho Chehab 	cmd.args[0x03] = 0x00;
9319a0bf528SMauro Carvalho Chehab 	cmd.args[0x04] = 0x8f;
9329a0bf528SMauro Carvalho Chehab 	cmd.args[0x05] = 0x28;
9339a0bf528SMauro Carvalho Chehab 	cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
9349a0bf528SMauro Carvalho Chehab 	cmd.args[0x07] = 0x01;
9359a0bf528SMauro Carvalho Chehab 	cmd.len = 0x08;
9369a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
9379a0bf528SMauro Carvalho Chehab 	if (ret != 0)
9389a0bf528SMauro Carvalho Chehab 		return ret;
9399a0bf528SMauro Carvalho Chehab 
9409a0bf528SMauro Carvalho Chehab 	/* Prepare a DiSEqC command */
9419a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[0x00] = CMD_LNBSEND;
9429a0bf528SMauro Carvalho Chehab 
9439a0bf528SMauro Carvalho Chehab 	/* DiSEqC burst */
9449a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_BURST]  = CX24116_DISEQC_MINI_A;
9459a0bf528SMauro Carvalho Chehab 
9469a0bf528SMauro Carvalho Chehab 	/* Unknown */
9479a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
9489a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
9499a0bf528SMauro Carvalho Chehab 	/* Continuation flag? */
9509a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00;
9519a0bf528SMauro Carvalho Chehab 
9529a0bf528SMauro Carvalho Chehab 	/* DiSEqC message length */
9539a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
9549a0bf528SMauro Carvalho Chehab 
9559a0bf528SMauro Carvalho Chehab 	/* Command length */
9569a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS;
9579a0bf528SMauro Carvalho Chehab 
9589a0bf528SMauro Carvalho Chehab 	return 0;
9599a0bf528SMauro Carvalho Chehab }
9609a0bf528SMauro Carvalho Chehab 
9619a0bf528SMauro Carvalho Chehab /* Send DiSEqC message with derived burst (hack) || previous burst */
9629a0bf528SMauro Carvalho Chehab static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
9639a0bf528SMauro Carvalho Chehab 	struct dvb_diseqc_master_cmd *d)
9649a0bf528SMauro Carvalho Chehab {
9659a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
9669a0bf528SMauro Carvalho Chehab 	int i, ret;
9679a0bf528SMauro Carvalho Chehab 
9681fa2337aSMauro Carvalho Chehab 	/* Validate length */
9691fa2337aSMauro Carvalho Chehab 	if (d->msg_len > sizeof(d->msg))
9701fa2337aSMauro Carvalho Chehab                 return -EINVAL;
9711fa2337aSMauro Carvalho Chehab 
9729a0bf528SMauro Carvalho Chehab 	/* Dump DiSEqC message */
9739a0bf528SMauro Carvalho Chehab 	if (debug) {
9749a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "cx24116: %s(", __func__);
9759a0bf528SMauro Carvalho Chehab 		for (i = 0 ; i < d->msg_len ;) {
9769a0bf528SMauro Carvalho Chehab 			printk(KERN_INFO "0x%02x", d->msg[i]);
9779a0bf528SMauro Carvalho Chehab 			if (++i < d->msg_len)
9789a0bf528SMauro Carvalho Chehab 				printk(KERN_INFO ", ");
9799a0bf528SMauro Carvalho Chehab 		}
9809a0bf528SMauro Carvalho Chehab 		printk(") toneburst=%d\n", toneburst);
9819a0bf528SMauro Carvalho Chehab 	}
9829a0bf528SMauro Carvalho Chehab 
9839a0bf528SMauro Carvalho Chehab 	/* DiSEqC message */
9849a0bf528SMauro Carvalho Chehab 	for (i = 0; i < d->msg_len; i++)
9859a0bf528SMauro Carvalho Chehab 		state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
9869a0bf528SMauro Carvalho Chehab 
9879a0bf528SMauro Carvalho Chehab 	/* DiSEqC message length */
9889a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
9899a0bf528SMauro Carvalho Chehab 
9909a0bf528SMauro Carvalho Chehab 	/* Command length */
9919a0bf528SMauro Carvalho Chehab 	state->dsec_cmd.len = CX24116_DISEQC_MSGOFS +
9929a0bf528SMauro Carvalho Chehab 		state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
9939a0bf528SMauro Carvalho Chehab 
9949a0bf528SMauro Carvalho Chehab 	/* DiSEqC toneburst */
9959a0bf528SMauro Carvalho Chehab 	if (toneburst == CX24116_DISEQC_MESGCACHE)
9969a0bf528SMauro Carvalho Chehab 		/* Message is cached */
9979a0bf528SMauro Carvalho Chehab 		return 0;
9989a0bf528SMauro Carvalho Chehab 
9999a0bf528SMauro Carvalho Chehab 	else if (toneburst == CX24116_DISEQC_TONEOFF)
10009a0bf528SMauro Carvalho Chehab 		/* Message is sent without burst */
10019a0bf528SMauro Carvalho Chehab 		state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
10029a0bf528SMauro Carvalho Chehab 
10039a0bf528SMauro Carvalho Chehab 	else if (toneburst == CX24116_DISEQC_TONECACHE) {
10049a0bf528SMauro Carvalho Chehab 		/*
10059a0bf528SMauro Carvalho Chehab 		 * Message is sent with derived else cached burst
10069a0bf528SMauro Carvalho Chehab 		 *
10079a0bf528SMauro Carvalho Chehab 		 * WRITE PORT GROUP COMMAND 38
10089a0bf528SMauro Carvalho Chehab 		 *
10099a0bf528SMauro Carvalho Chehab 		 * 0/A/A: E0 10 38 F0..F3
10109a0bf528SMauro Carvalho Chehab 		 * 1/B/B: E0 10 38 F4..F7
10119a0bf528SMauro Carvalho Chehab 		 * 2/C/A: E0 10 38 F8..FB
10129a0bf528SMauro Carvalho Chehab 		 * 3/D/B: E0 10 38 FC..FF
10139a0bf528SMauro Carvalho Chehab 		 *
10149a0bf528SMauro Carvalho Chehab 		 * databyte[3]= 8421:8421
10159a0bf528SMauro Carvalho Chehab 		 *              ABCD:WXYZ
10169a0bf528SMauro Carvalho Chehab 		 *              CLR :SET
10179a0bf528SMauro Carvalho Chehab 		 *
10189a0bf528SMauro Carvalho Chehab 		 *              WX= PORT SELECT 0..3    (X=TONEBURST)
10199a0bf528SMauro Carvalho Chehab 		 *              Y = VOLTAGE             (0=13V, 1=18V)
10209a0bf528SMauro Carvalho Chehab 		 *              Z = BAND                (0=LOW, 1=HIGH(22K))
10219a0bf528SMauro Carvalho Chehab 		 */
10229a0bf528SMauro Carvalho Chehab 		if (d->msg_len >= 4 && d->msg[2] == 0x38)
10239a0bf528SMauro Carvalho Chehab 			state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10249a0bf528SMauro Carvalho Chehab 				((d->msg[3] & 4) >> 2);
10259a0bf528SMauro Carvalho Chehab 		if (debug)
10269a0bf528SMauro Carvalho Chehab 			dprintk("%s burst=%d\n", __func__,
10279a0bf528SMauro Carvalho Chehab 				state->dsec_cmd.args[CX24116_DISEQC_BURST]);
10289a0bf528SMauro Carvalho Chehab 	}
10299a0bf528SMauro Carvalho Chehab 
10309a0bf528SMauro Carvalho Chehab 	/* Wait for LNB ready */
10319a0bf528SMauro Carvalho Chehab 	ret = cx24116_wait_for_lnb(fe);
10329a0bf528SMauro Carvalho Chehab 	if (ret != 0)
10339a0bf528SMauro Carvalho Chehab 		return ret;
10349a0bf528SMauro Carvalho Chehab 
10359a0bf528SMauro Carvalho Chehab 	/* Wait for voltage/min repeat delay */
10369a0bf528SMauro Carvalho Chehab 	msleep(100);
10379a0bf528SMauro Carvalho Chehab 
10389a0bf528SMauro Carvalho Chehab 	/* Command */
10399a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
10409a0bf528SMauro Carvalho Chehab 	if (ret != 0)
10419a0bf528SMauro Carvalho Chehab 		return ret;
10429a0bf528SMauro Carvalho Chehab 	/*
10439a0bf528SMauro Carvalho Chehab 	 * Wait for send
10449a0bf528SMauro Carvalho Chehab 	 *
10459a0bf528SMauro Carvalho Chehab 	 * Eutelsat spec:
10469a0bf528SMauro Carvalho Chehab 	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
10479a0bf528SMauro Carvalho Chehab 	 *  13.5ms per byte     +
10489a0bf528SMauro Carvalho Chehab 	 * >15ms delay          +
10499a0bf528SMauro Carvalho Chehab 	 *  12.5ms burst        +
10509a0bf528SMauro Carvalho Chehab 	 * >15ms delay            (XXX determine if FW does this, see set_tone)
10519a0bf528SMauro Carvalho Chehab 	 */
10529a0bf528SMauro Carvalho Chehab 	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) +
10539a0bf528SMauro Carvalho Chehab 		((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60));
10549a0bf528SMauro Carvalho Chehab 
10559a0bf528SMauro Carvalho Chehab 	return 0;
10569a0bf528SMauro Carvalho Chehab }
10579a0bf528SMauro Carvalho Chehab 
10589a0bf528SMauro Carvalho Chehab /* Send DiSEqC burst */
10599a0bf528SMauro Carvalho Chehab static int cx24116_diseqc_send_burst(struct dvb_frontend *fe,
10600df289a2SMauro Carvalho Chehab 	enum fe_sec_mini_cmd burst)
10619a0bf528SMauro Carvalho Chehab {
10629a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
10639a0bf528SMauro Carvalho Chehab 	int ret;
10649a0bf528SMauro Carvalho Chehab 
10659a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst);
10669a0bf528SMauro Carvalho Chehab 
10679a0bf528SMauro Carvalho Chehab 	/* DiSEqC burst */
10689a0bf528SMauro Carvalho Chehab 	if (burst == SEC_MINI_A)
10699a0bf528SMauro Carvalho Chehab 		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10709a0bf528SMauro Carvalho Chehab 			CX24116_DISEQC_MINI_A;
10719a0bf528SMauro Carvalho Chehab 	else if (burst == SEC_MINI_B)
10729a0bf528SMauro Carvalho Chehab 		state->dsec_cmd.args[CX24116_DISEQC_BURST] =
10739a0bf528SMauro Carvalho Chehab 			CX24116_DISEQC_MINI_B;
10749a0bf528SMauro Carvalho Chehab 	else
10759a0bf528SMauro Carvalho Chehab 		return -EINVAL;
10769a0bf528SMauro Carvalho Chehab 
10779a0bf528SMauro Carvalho Chehab 	/* DiSEqC toneburst */
10789a0bf528SMauro Carvalho Chehab 	if (toneburst != CX24116_DISEQC_MESGCACHE)
10799a0bf528SMauro Carvalho Chehab 		/* Burst is cached */
10809a0bf528SMauro Carvalho Chehab 		return 0;
10819a0bf528SMauro Carvalho Chehab 
10829a0bf528SMauro Carvalho Chehab 	/* Burst is to be sent with cached message */
10839a0bf528SMauro Carvalho Chehab 
10849a0bf528SMauro Carvalho Chehab 	/* Wait for LNB ready */
10859a0bf528SMauro Carvalho Chehab 	ret = cx24116_wait_for_lnb(fe);
10869a0bf528SMauro Carvalho Chehab 	if (ret != 0)
10879a0bf528SMauro Carvalho Chehab 		return ret;
10889a0bf528SMauro Carvalho Chehab 
10899a0bf528SMauro Carvalho Chehab 	/* Wait for voltage/min repeat delay */
10909a0bf528SMauro Carvalho Chehab 	msleep(100);
10919a0bf528SMauro Carvalho Chehab 
10929a0bf528SMauro Carvalho Chehab 	/* Command */
10939a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
10949a0bf528SMauro Carvalho Chehab 	if (ret != 0)
10959a0bf528SMauro Carvalho Chehab 		return ret;
10969a0bf528SMauro Carvalho Chehab 
10979a0bf528SMauro Carvalho Chehab 	/*
10989a0bf528SMauro Carvalho Chehab 	 * Wait for send
10999a0bf528SMauro Carvalho Chehab 	 *
11009a0bf528SMauro Carvalho Chehab 	 * Eutelsat spec:
11019a0bf528SMauro Carvalho Chehab 	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
11029a0bf528SMauro Carvalho Chehab 	 *  13.5ms per byte     +
11039a0bf528SMauro Carvalho Chehab 	 * >15ms delay          +
11049a0bf528SMauro Carvalho Chehab 	 *  12.5ms burst        +
11059a0bf528SMauro Carvalho Chehab 	 * >15ms delay            (XXX determine if FW does this, see set_tone)
11069a0bf528SMauro Carvalho Chehab 	 */
11079a0bf528SMauro Carvalho Chehab 	msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60);
11089a0bf528SMauro Carvalho Chehab 
11099a0bf528SMauro Carvalho Chehab 	return 0;
11109a0bf528SMauro Carvalho Chehab }
11119a0bf528SMauro Carvalho Chehab 
11129a0bf528SMauro Carvalho Chehab static void cx24116_release(struct dvb_frontend *fe)
11139a0bf528SMauro Carvalho Chehab {
11149a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
11159a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
11169a0bf528SMauro Carvalho Chehab 	kfree(state);
11179a0bf528SMauro Carvalho Chehab }
11189a0bf528SMauro Carvalho Chehab 
1119*bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops;
11209a0bf528SMauro Carvalho Chehab 
11219a0bf528SMauro Carvalho Chehab struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
11229a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c)
11239a0bf528SMauro Carvalho Chehab {
11249a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = NULL;
11259a0bf528SMauro Carvalho Chehab 	int ret;
11269a0bf528SMauro Carvalho Chehab 
11279a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
11289a0bf528SMauro Carvalho Chehab 
11299a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
11309a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL);
11319a0bf528SMauro Carvalho Chehab 	if (state == NULL)
11329a0bf528SMauro Carvalho Chehab 		goto error1;
11339a0bf528SMauro Carvalho Chehab 
11349a0bf528SMauro Carvalho Chehab 	state->config = config;
11359a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
11369a0bf528SMauro Carvalho Chehab 
11379a0bf528SMauro Carvalho Chehab 	/* check if the demod is present */
11389a0bf528SMauro Carvalho Chehab 	ret = (cx24116_readreg(state, 0xFF) << 8) |
11399a0bf528SMauro Carvalho Chehab 		cx24116_readreg(state, 0xFE);
11409a0bf528SMauro Carvalho Chehab 	if (ret != 0x0501) {
11419a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n");
11429a0bf528SMauro Carvalho Chehab 		goto error2;
11439a0bf528SMauro Carvalho Chehab 	}
11449a0bf528SMauro Carvalho Chehab 
11459a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
11469a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &cx24116_ops,
11479a0bf528SMauro Carvalho Chehab 		sizeof(struct dvb_frontend_ops));
11489a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
11499a0bf528SMauro Carvalho Chehab 	return &state->frontend;
11509a0bf528SMauro Carvalho Chehab 
11519a0bf528SMauro Carvalho Chehab error2: kfree(state);
11529a0bf528SMauro Carvalho Chehab error1: return NULL;
11539a0bf528SMauro Carvalho Chehab }
11549a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(cx24116_attach);
11559a0bf528SMauro Carvalho Chehab 
11569a0bf528SMauro Carvalho Chehab /*
11579a0bf528SMauro Carvalho Chehab  * Initialise or wake up device
11589a0bf528SMauro Carvalho Chehab  *
11599a0bf528SMauro Carvalho Chehab  * Power config will reset and load initial firmware if required
11609a0bf528SMauro Carvalho Chehab  */
11619a0bf528SMauro Carvalho Chehab static int cx24116_initfe(struct dvb_frontend *fe)
11629a0bf528SMauro Carvalho Chehab {
11639a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
11649a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
11659a0bf528SMauro Carvalho Chehab 	int ret;
11669a0bf528SMauro Carvalho Chehab 
11679a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
11689a0bf528SMauro Carvalho Chehab 
11699a0bf528SMauro Carvalho Chehab 	/* Power on */
11709a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe0, 0);
11719a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe1, 0);
11729a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xea, 0);
11739a0bf528SMauro Carvalho Chehab 
11749a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 36: Power config */
11759a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_TUNERSLEEP;
11769a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0;
11779a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
11789a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
11799a0bf528SMauro Carvalho Chehab 	if (ret != 0)
11809a0bf528SMauro Carvalho Chehab 		return ret;
11819a0bf528SMauro Carvalho Chehab 
11829a0bf528SMauro Carvalho Chehab 	ret = cx24116_diseqc_init(fe);
11839a0bf528SMauro Carvalho Chehab 	if (ret != 0)
11849a0bf528SMauro Carvalho Chehab 		return ret;
11859a0bf528SMauro Carvalho Chehab 
11869a0bf528SMauro Carvalho Chehab 	/* HVR-4000 needs this */
11879a0bf528SMauro Carvalho Chehab 	return cx24116_set_voltage(fe, SEC_VOLTAGE_13);
11889a0bf528SMauro Carvalho Chehab }
11899a0bf528SMauro Carvalho Chehab 
11909a0bf528SMauro Carvalho Chehab /*
11919a0bf528SMauro Carvalho Chehab  * Put device to sleep
11929a0bf528SMauro Carvalho Chehab  */
11939a0bf528SMauro Carvalho Chehab static int cx24116_sleep(struct dvb_frontend *fe)
11949a0bf528SMauro Carvalho Chehab {
11959a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
11969a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
11979a0bf528SMauro Carvalho Chehab 	int ret;
11989a0bf528SMauro Carvalho Chehab 
11999a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
12009a0bf528SMauro Carvalho Chehab 
12019a0bf528SMauro Carvalho Chehab 	/* Firmware CMD 36: Power config */
12029a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_TUNERSLEEP;
12039a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 1;
12049a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
12059a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
12069a0bf528SMauro Carvalho Chehab 	if (ret != 0)
12079a0bf528SMauro Carvalho Chehab 		return ret;
12089a0bf528SMauro Carvalho Chehab 
12099a0bf528SMauro Carvalho Chehab 	/* Power off (Shutdown clocks) */
12109a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xea, 0xff);
12119a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe1, 1);
12129a0bf528SMauro Carvalho Chehab 	cx24116_writereg(state, 0xe0, 1);
12139a0bf528SMauro Carvalho Chehab 
12149a0bf528SMauro Carvalho Chehab 	return 0;
12159a0bf528SMauro Carvalho Chehab }
12169a0bf528SMauro Carvalho Chehab 
12179a0bf528SMauro Carvalho Chehab /* dvb-core told us to tune, the tv property cache will be complete,
12189a0bf528SMauro Carvalho Chehab  * it's safe for is to pull values and use them for tuning purposes.
12199a0bf528SMauro Carvalho Chehab  */
12209a0bf528SMauro Carvalho Chehab static int cx24116_set_frontend(struct dvb_frontend *fe)
12219a0bf528SMauro Carvalho Chehab {
12229a0bf528SMauro Carvalho Chehab 	struct cx24116_state *state = fe->demodulator_priv;
12239a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
12249a0bf528SMauro Carvalho Chehab 	struct cx24116_cmd cmd;
12250df289a2SMauro Carvalho Chehab 	enum fe_status tunerstat;
12269a0bf528SMauro Carvalho Chehab 	int i, status, ret, retune = 1;
12279a0bf528SMauro Carvalho Chehab 
12289a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
12299a0bf528SMauro Carvalho Chehab 
12309a0bf528SMauro Carvalho Chehab 	switch (c->delivery_system) {
12319a0bf528SMauro Carvalho Chehab 	case SYS_DVBS:
12329a0bf528SMauro Carvalho Chehab 		dprintk("%s: DVB-S delivery system selected\n", __func__);
12339a0bf528SMauro Carvalho Chehab 
12349a0bf528SMauro Carvalho Chehab 		/* Only QPSK is supported for DVB-S */
12359a0bf528SMauro Carvalho Chehab 		if (c->modulation != QPSK) {
12369a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported modulation selected (%d)\n",
12379a0bf528SMauro Carvalho Chehab 				__func__, c->modulation);
12389a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
12399a0bf528SMauro Carvalho Chehab 		}
12409a0bf528SMauro Carvalho Chehab 
12419a0bf528SMauro Carvalho Chehab 		/* Pilot doesn't exist in DVB-S, turn bit off */
12429a0bf528SMauro Carvalho Chehab 		state->dnxt.pilot_val = CX24116_PILOT_OFF;
12439a0bf528SMauro Carvalho Chehab 
12449a0bf528SMauro Carvalho Chehab 		/* DVB-S only supports 0.35 */
12459a0bf528SMauro Carvalho Chehab 		if (c->rolloff != ROLLOFF_35) {
12469a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported rolloff selected (%d)\n",
12479a0bf528SMauro Carvalho Chehab 				__func__, c->rolloff);
12489a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
12499a0bf528SMauro Carvalho Chehab 		}
12509a0bf528SMauro Carvalho Chehab 		state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
12519a0bf528SMauro Carvalho Chehab 		break;
12529a0bf528SMauro Carvalho Chehab 
12539a0bf528SMauro Carvalho Chehab 	case SYS_DVBS2:
12549a0bf528SMauro Carvalho Chehab 		dprintk("%s: DVB-S2 delivery system selected\n", __func__);
12559a0bf528SMauro Carvalho Chehab 
12569a0bf528SMauro Carvalho Chehab 		/*
12579a0bf528SMauro Carvalho Chehab 		 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
12589a0bf528SMauro Carvalho Chehab 		 * but not hardware auto detection
12599a0bf528SMauro Carvalho Chehab 		 */
12609a0bf528SMauro Carvalho Chehab 		if (c->modulation != PSK_8 && c->modulation != QPSK) {
12619a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported modulation selected (%d)\n",
12629a0bf528SMauro Carvalho Chehab 				__func__, c->modulation);
12639a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
12649a0bf528SMauro Carvalho Chehab 		}
12659a0bf528SMauro Carvalho Chehab 
12669a0bf528SMauro Carvalho Chehab 		switch (c->pilot) {
12679a0bf528SMauro Carvalho Chehab 		case PILOT_AUTO:	/* Not supported but emulated */
12689a0bf528SMauro Carvalho Chehab 			state->dnxt.pilot_val = (c->modulation == QPSK)
12699a0bf528SMauro Carvalho Chehab 				? CX24116_PILOT_OFF : CX24116_PILOT_ON;
12709a0bf528SMauro Carvalho Chehab 			retune++;
12719a0bf528SMauro Carvalho Chehab 			break;
12729a0bf528SMauro Carvalho Chehab 		case PILOT_OFF:
12739a0bf528SMauro Carvalho Chehab 			state->dnxt.pilot_val = CX24116_PILOT_OFF;
12749a0bf528SMauro Carvalho Chehab 			break;
12759a0bf528SMauro Carvalho Chehab 		case PILOT_ON:
12769a0bf528SMauro Carvalho Chehab 			state->dnxt.pilot_val = CX24116_PILOT_ON;
12779a0bf528SMauro Carvalho Chehab 			break;
12789a0bf528SMauro Carvalho Chehab 		default:
12799a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported pilot mode selected (%d)\n",
12809a0bf528SMauro Carvalho Chehab 				__func__, c->pilot);
12819a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
12829a0bf528SMauro Carvalho Chehab 		}
12839a0bf528SMauro Carvalho Chehab 
12849a0bf528SMauro Carvalho Chehab 		switch (c->rolloff) {
12859a0bf528SMauro Carvalho Chehab 		case ROLLOFF_20:
12869a0bf528SMauro Carvalho Chehab 			state->dnxt.rolloff_val = CX24116_ROLLOFF_020;
12879a0bf528SMauro Carvalho Chehab 			break;
12889a0bf528SMauro Carvalho Chehab 		case ROLLOFF_25:
12899a0bf528SMauro Carvalho Chehab 			state->dnxt.rolloff_val = CX24116_ROLLOFF_025;
12909a0bf528SMauro Carvalho Chehab 			break;
12919a0bf528SMauro Carvalho Chehab 		case ROLLOFF_35:
12929a0bf528SMauro Carvalho Chehab 			state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
12939a0bf528SMauro Carvalho Chehab 			break;
12949a0bf528SMauro Carvalho Chehab 		case ROLLOFF_AUTO:	/* Rolloff must be explicit */
12959a0bf528SMauro Carvalho Chehab 		default:
12969a0bf528SMauro Carvalho Chehab 			dprintk("%s: unsupported rolloff selected (%d)\n",
12979a0bf528SMauro Carvalho Chehab 				__func__, c->rolloff);
12989a0bf528SMauro Carvalho Chehab 			return -EOPNOTSUPP;
12999a0bf528SMauro Carvalho Chehab 		}
13009a0bf528SMauro Carvalho Chehab 		break;
13019a0bf528SMauro Carvalho Chehab 
13029a0bf528SMauro Carvalho Chehab 	default:
13039a0bf528SMauro Carvalho Chehab 		dprintk("%s: unsupported delivery system selected (%d)\n",
13049a0bf528SMauro Carvalho Chehab 			__func__, c->delivery_system);
13059a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
13069a0bf528SMauro Carvalho Chehab 	}
13079a0bf528SMauro Carvalho Chehab 	state->dnxt.delsys = c->delivery_system;
13089a0bf528SMauro Carvalho Chehab 	state->dnxt.modulation = c->modulation;
13099a0bf528SMauro Carvalho Chehab 	state->dnxt.frequency = c->frequency;
13109a0bf528SMauro Carvalho Chehab 	state->dnxt.pilot = c->pilot;
13119a0bf528SMauro Carvalho Chehab 	state->dnxt.rolloff = c->rolloff;
13129a0bf528SMauro Carvalho Chehab 
13139a0bf528SMauro Carvalho Chehab 	ret = cx24116_set_inversion(state, c->inversion);
13149a0bf528SMauro Carvalho Chehab 	if (ret !=  0)
13159a0bf528SMauro Carvalho Chehab 		return ret;
13169a0bf528SMauro Carvalho Chehab 
13179a0bf528SMauro Carvalho Chehab 	/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
13189a0bf528SMauro Carvalho Chehab 	ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner);
13199a0bf528SMauro Carvalho Chehab 	if (ret !=  0)
13209a0bf528SMauro Carvalho Chehab 		return ret;
13219a0bf528SMauro Carvalho Chehab 
13229a0bf528SMauro Carvalho Chehab 	ret = cx24116_set_symbolrate(state, c->symbol_rate);
13239a0bf528SMauro Carvalho Chehab 	if (ret !=  0)
13249a0bf528SMauro Carvalho Chehab 		return ret;
13259a0bf528SMauro Carvalho Chehab 
13269a0bf528SMauro Carvalho Chehab 	/* discard the 'current' tuning parameters and prepare to tune */
13279a0bf528SMauro Carvalho Chehab 	cx24116_clone_params(fe);
13289a0bf528SMauro Carvalho Chehab 
13299a0bf528SMauro Carvalho Chehab 	dprintk("%s:   delsys      = %d\n", __func__, state->dcur.delsys);
13309a0bf528SMauro Carvalho Chehab 	dprintk("%s:   modulation  = %d\n", __func__, state->dcur.modulation);
13319a0bf528SMauro Carvalho Chehab 	dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);
13329a0bf528SMauro Carvalho Chehab 	dprintk("%s:   pilot       = %d (val = 0x%02x)\n", __func__,
13339a0bf528SMauro Carvalho Chehab 		state->dcur.pilot, state->dcur.pilot_val);
13349a0bf528SMauro Carvalho Chehab 	dprintk("%s:   retune      = %d\n", __func__, retune);
13359a0bf528SMauro Carvalho Chehab 	dprintk("%s:   rolloff     = %d (val = 0x%02x)\n", __func__,
13369a0bf528SMauro Carvalho Chehab 		state->dcur.rolloff, state->dcur.rolloff_val);
13379a0bf528SMauro Carvalho Chehab 	dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
13389a0bf528SMauro Carvalho Chehab 	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
13399a0bf528SMauro Carvalho Chehab 		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
13409a0bf528SMauro Carvalho Chehab 	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __func__,
13419a0bf528SMauro Carvalho Chehab 		state->dcur.inversion, state->dcur.inversion_val);
13429a0bf528SMauro Carvalho Chehab 
13439a0bf528SMauro Carvalho Chehab 	/* This is also done in advise/acquire on HVR4000 but not on LITE */
13449a0bf528SMauro Carvalho Chehab 	if (state->config->set_ts_params)
13459a0bf528SMauro Carvalho Chehab 		state->config->set_ts_params(fe, 0);
13469a0bf528SMauro Carvalho Chehab 
13479a0bf528SMauro Carvalho Chehab 	/* Set/Reset B/W */
13489a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_BANDWIDTH;
13499a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x01;
13509a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
13519a0bf528SMauro Carvalho Chehab 	ret = cx24116_cmd_execute(fe, &cmd);
13529a0bf528SMauro Carvalho Chehab 	if (ret != 0)
13539a0bf528SMauro Carvalho Chehab 		return ret;
13549a0bf528SMauro Carvalho Chehab 
13559a0bf528SMauro Carvalho Chehab 	/* Prepare a tune request */
13569a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_TUNEREQUEST;
13579a0bf528SMauro Carvalho Chehab 
13589a0bf528SMauro Carvalho Chehab 	/* Frequency */
13599a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
13609a0bf528SMauro Carvalho Chehab 	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
13619a0bf528SMauro Carvalho Chehab 	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
13629a0bf528SMauro Carvalho Chehab 
13639a0bf528SMauro Carvalho Chehab 	/* Symbol Rate */
13649a0bf528SMauro Carvalho Chehab 	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
13659a0bf528SMauro Carvalho Chehab 	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
13669a0bf528SMauro Carvalho Chehab 
13679a0bf528SMauro Carvalho Chehab 	/* Automatic Inversion */
13689a0bf528SMauro Carvalho Chehab 	cmd.args[0x06] = state->dcur.inversion_val;
13699a0bf528SMauro Carvalho Chehab 
13709a0bf528SMauro Carvalho Chehab 	/* Modulation / FEC / Pilot */
13719a0bf528SMauro Carvalho Chehab 	cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;
13729a0bf528SMauro Carvalho Chehab 
13739a0bf528SMauro Carvalho Chehab 	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
13749a0bf528SMauro Carvalho Chehab 	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
13759a0bf528SMauro Carvalho Chehab 	cmd.args[0x0a] = 0x00;
13769a0bf528SMauro Carvalho Chehab 	cmd.args[0x0b] = 0x00;
13779a0bf528SMauro Carvalho Chehab 	cmd.args[0x0c] = state->dcur.rolloff_val;
13789a0bf528SMauro Carvalho Chehab 	cmd.args[0x0d] = state->dcur.fec_mask;
13799a0bf528SMauro Carvalho Chehab 
13809a0bf528SMauro Carvalho Chehab 	if (state->dcur.symbol_rate > 30000000) {
13819a0bf528SMauro Carvalho Chehab 		cmd.args[0x0e] = 0x04;
13829a0bf528SMauro Carvalho Chehab 		cmd.args[0x0f] = 0x00;
13839a0bf528SMauro Carvalho Chehab 		cmd.args[0x10] = 0x01;
13849a0bf528SMauro Carvalho Chehab 		cmd.args[0x11] = 0x77;
13859a0bf528SMauro Carvalho Chehab 		cmd.args[0x12] = 0x36;
13869a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
13879a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
13889a0bf528SMauro Carvalho Chehab 	} else {
13899a0bf528SMauro Carvalho Chehab 		cmd.args[0x0e] = 0x06;
13909a0bf528SMauro Carvalho Chehab 		cmd.args[0x0f] = 0x00;
13919a0bf528SMauro Carvalho Chehab 		cmd.args[0x10] = 0x00;
13929a0bf528SMauro Carvalho Chehab 		cmd.args[0x11] = 0xFA;
13939a0bf528SMauro Carvalho Chehab 		cmd.args[0x12] = 0x24;
13949a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
13959a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
13969a0bf528SMauro Carvalho Chehab 	}
13979a0bf528SMauro Carvalho Chehab 
13989a0bf528SMauro Carvalho Chehab 	cmd.len = 0x13;
13999a0bf528SMauro Carvalho Chehab 
14009a0bf528SMauro Carvalho Chehab 	/* We need to support pilot and non-pilot tuning in the
14019a0bf528SMauro Carvalho Chehab 	 * driver automatically. This is a workaround for because
14029a0bf528SMauro Carvalho Chehab 	 * the demod does not support autodetect.
14039a0bf528SMauro Carvalho Chehab 	 */
14049a0bf528SMauro Carvalho Chehab 	do {
14059a0bf528SMauro Carvalho Chehab 		/* Reset status register */
14069a0bf528SMauro Carvalho Chehab 		status = cx24116_readreg(state, CX24116_REG_SSTATUS)
14079a0bf528SMauro Carvalho Chehab 			& CX24116_SIGNAL_MASK;
14089a0bf528SMauro Carvalho Chehab 		cx24116_writereg(state, CX24116_REG_SSTATUS, status);
14099a0bf528SMauro Carvalho Chehab 
14109a0bf528SMauro Carvalho Chehab 		/* Tune */
14119a0bf528SMauro Carvalho Chehab 		ret = cx24116_cmd_execute(fe, &cmd);
14129a0bf528SMauro Carvalho Chehab 		if (ret != 0)
14139a0bf528SMauro Carvalho Chehab 			break;
14149a0bf528SMauro Carvalho Chehab 
14159a0bf528SMauro Carvalho Chehab 		/*
14169a0bf528SMauro Carvalho Chehab 		 * Wait for up to 500 ms before retrying
14179a0bf528SMauro Carvalho Chehab 		 *
14189a0bf528SMauro Carvalho Chehab 		 * If we are able to tune then generally it occurs within 100ms.
14199a0bf528SMauro Carvalho Chehab 		 * If it takes longer, try a different toneburst setting.
14209a0bf528SMauro Carvalho Chehab 		 */
14219a0bf528SMauro Carvalho Chehab 		for (i = 0; i < 50 ; i++) {
14229a0bf528SMauro Carvalho Chehab 			cx24116_read_status(fe, &tunerstat);
14239a0bf528SMauro Carvalho Chehab 			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
14249a0bf528SMauro Carvalho Chehab 			if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
14259a0bf528SMauro Carvalho Chehab 				dprintk("%s: Tuned\n", __func__);
14269a0bf528SMauro Carvalho Chehab 				goto tuned;
14279a0bf528SMauro Carvalho Chehab 			}
14289a0bf528SMauro Carvalho Chehab 			msleep(10);
14299a0bf528SMauro Carvalho Chehab 		}
14309a0bf528SMauro Carvalho Chehab 
14319a0bf528SMauro Carvalho Chehab 		dprintk("%s: Not tuned\n", __func__);
14329a0bf528SMauro Carvalho Chehab 
14339a0bf528SMauro Carvalho Chehab 		/* Toggle pilot bit when in auto-pilot */
14349a0bf528SMauro Carvalho Chehab 		if (state->dcur.pilot == PILOT_AUTO)
14359a0bf528SMauro Carvalho Chehab 			cmd.args[0x07] ^= CX24116_PILOT_ON;
14369a0bf528SMauro Carvalho Chehab 	} while (--retune);
14379a0bf528SMauro Carvalho Chehab 
14389a0bf528SMauro Carvalho Chehab tuned:  /* Set/Reset B/W */
14399a0bf528SMauro Carvalho Chehab 	cmd.args[0x00] = CMD_BANDWIDTH;
14409a0bf528SMauro Carvalho Chehab 	cmd.args[0x01] = 0x00;
14419a0bf528SMauro Carvalho Chehab 	cmd.len = 0x02;
14429a0bf528SMauro Carvalho Chehab 	return cx24116_cmd_execute(fe, &cmd);
14439a0bf528SMauro Carvalho Chehab }
14449a0bf528SMauro Carvalho Chehab 
14459a0bf528SMauro Carvalho Chehab static int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
14460df289a2SMauro Carvalho Chehab 	unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
14479a0bf528SMauro Carvalho Chehab {
14489a0bf528SMauro Carvalho Chehab 	/*
14499a0bf528SMauro Carvalho Chehab 	 * It is safe to discard "params" here, as the DVB core will sync
14509a0bf528SMauro Carvalho Chehab 	 * fe->dtv_property_cache with fepriv->parameters_in, where the
14519a0bf528SMauro Carvalho Chehab 	 * DVBv3 params are stored. The only practical usage for it indicate
14529a0bf528SMauro Carvalho Chehab 	 * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is
14539a0bf528SMauro Carvalho Chehab 	 * true.
14549a0bf528SMauro Carvalho Chehab 	 */
14559a0bf528SMauro Carvalho Chehab 
14569a0bf528SMauro Carvalho Chehab 	*delay = HZ / 5;
14579a0bf528SMauro Carvalho Chehab 	if (re_tune) {
14589a0bf528SMauro Carvalho Chehab 		int ret = cx24116_set_frontend(fe);
14599a0bf528SMauro Carvalho Chehab 		if (ret)
14609a0bf528SMauro Carvalho Chehab 			return ret;
14619a0bf528SMauro Carvalho Chehab 	}
14629a0bf528SMauro Carvalho Chehab 	return cx24116_read_status(fe, status);
14639a0bf528SMauro Carvalho Chehab }
14649a0bf528SMauro Carvalho Chehab 
14659a0bf528SMauro Carvalho Chehab static int cx24116_get_algo(struct dvb_frontend *fe)
14669a0bf528SMauro Carvalho Chehab {
14679a0bf528SMauro Carvalho Chehab 	return DVBFE_ALGO_HW;
14689a0bf528SMauro Carvalho Chehab }
14699a0bf528SMauro Carvalho Chehab 
1470*bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops = {
14719a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS, SYS_DVBS2 },
14729a0bf528SMauro Carvalho Chehab 	.info = {
14739a0bf528SMauro Carvalho Chehab 		.name = "Conexant CX24116/CX24118",
14749a0bf528SMauro Carvalho Chehab 		.frequency_min = 950000,
14759a0bf528SMauro Carvalho Chehab 		.frequency_max = 2150000,
14769a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
14779a0bf528SMauro Carvalho Chehab 		.frequency_tolerance = 5000,
14789a0bf528SMauro Carvalho Chehab 		.symbol_rate_min = 1000000,
14799a0bf528SMauro Carvalho Chehab 		.symbol_rate_max = 45000000,
14809a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
14819a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
14829a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
14839a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
14849a0bf528SMauro Carvalho Chehab 			FE_CAN_2G_MODULATION |
14859a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK | FE_CAN_RECOVER
14869a0bf528SMauro Carvalho Chehab 	},
14879a0bf528SMauro Carvalho Chehab 
14889a0bf528SMauro Carvalho Chehab 	.release = cx24116_release,
14899a0bf528SMauro Carvalho Chehab 
14909a0bf528SMauro Carvalho Chehab 	.init = cx24116_initfe,
14919a0bf528SMauro Carvalho Chehab 	.sleep = cx24116_sleep,
14929a0bf528SMauro Carvalho Chehab 	.read_status = cx24116_read_status,
14939a0bf528SMauro Carvalho Chehab 	.read_ber = cx24116_read_ber,
14949a0bf528SMauro Carvalho Chehab 	.read_signal_strength = cx24116_read_signal_strength,
14959a0bf528SMauro Carvalho Chehab 	.read_snr = cx24116_read_snr,
14969a0bf528SMauro Carvalho Chehab 	.read_ucblocks = cx24116_read_ucblocks,
14979a0bf528SMauro Carvalho Chehab 	.set_tone = cx24116_set_tone,
14989a0bf528SMauro Carvalho Chehab 	.set_voltage = cx24116_set_voltage,
14999a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
15009a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = cx24116_diseqc_send_burst,
15019a0bf528SMauro Carvalho Chehab 	.get_frontend_algo = cx24116_get_algo,
15029a0bf528SMauro Carvalho Chehab 	.tune = cx24116_tune,
15039a0bf528SMauro Carvalho Chehab 
15049a0bf528SMauro Carvalho Chehab 	.set_frontend = cx24116_set_frontend,
15059a0bf528SMauro Carvalho Chehab };
15069a0bf528SMauro Carvalho Chehab 
15079a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
15089a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth");
15099a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
15109a0bf528SMauro Carvalho Chehab 
1511