19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab  *   Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver
39a0bf528SMauro Carvalho Chehab  *
49a0bf528SMauro Carvalho Chehab  *   Copyright (C) 2005 Steven Toth <stoth@linuxtv.org>
59a0bf528SMauro Carvalho Chehab  *
69a0bf528SMauro Carvalho Chehab  *   Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc>
79a0bf528SMauro Carvalho Chehab  *
89a0bf528SMauro Carvalho Chehab  *   Support for CX24123/CX24113-NIM by Patrick Boettcher <pb@linuxtv.org>
99a0bf528SMauro Carvalho Chehab  *
109a0bf528SMauro Carvalho Chehab  *   This program is free software; you can redistribute it and/or
119a0bf528SMauro Carvalho Chehab  *   modify it under the terms of the GNU General Public License as
129a0bf528SMauro Carvalho Chehab  *   published by the Free Software Foundation; either version 2 of
139a0bf528SMauro Carvalho Chehab  *   the License, or (at your option) any later version.
149a0bf528SMauro Carvalho Chehab  *
159a0bf528SMauro Carvalho Chehab  *   This program is distributed in the hope that it will be useful,
169a0bf528SMauro Carvalho Chehab  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
179a0bf528SMauro Carvalho Chehab  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
189a0bf528SMauro Carvalho Chehab  *   General Public License for more details.
199a0bf528SMauro Carvalho Chehab  *
209a0bf528SMauro Carvalho Chehab  *   You should have received a copy of the GNU General Public License
219a0bf528SMauro Carvalho Chehab  *   along with this program; if not, write to the Free Software
229a0bf528SMauro Carvalho Chehab  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
239a0bf528SMauro Carvalho Chehab  */
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
269a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
279a0bf528SMauro Carvalho Chehab #include <linux/module.h>
289a0bf528SMauro Carvalho Chehab #include <linux/init.h>
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
319a0bf528SMauro Carvalho Chehab #include "cx24123.h"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab #define XTAL 10111000
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab static int force_band;
369a0bf528SMauro Carvalho Chehab module_param(force_band, int, 0644);
379a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(force_band, "Force a specific band select "\
389a0bf528SMauro Carvalho Chehab 	"(1-9, default:off).");
399a0bf528SMauro Carvalho Chehab 
409a0bf528SMauro Carvalho Chehab static int debug;
419a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
429a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
439a0bf528SMauro Carvalho Chehab 
449a0bf528SMauro Carvalho Chehab #define info(args...) do { printk(KERN_INFO "CX24123: " args); } while (0)
459a0bf528SMauro Carvalho Chehab #define err(args...)  do { printk(KERN_ERR  "CX24123: " args); } while (0)
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
489a0bf528SMauro Carvalho Chehab 	do { \
499a0bf528SMauro Carvalho Chehab 		if (debug) { \
509a0bf528SMauro Carvalho Chehab 			printk(KERN_DEBUG "CX24123: %s: ", __func__); \
519a0bf528SMauro Carvalho Chehab 			printk(args); \
529a0bf528SMauro Carvalho Chehab 		} \
539a0bf528SMauro Carvalho Chehab 	} while (0)
549a0bf528SMauro Carvalho Chehab 
559a0bf528SMauro Carvalho Chehab struct cx24123_state {
569a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
579a0bf528SMauro Carvalho Chehab 	const struct cx24123_config *config;
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 	/* Some PLL specifics for tuning */
629a0bf528SMauro Carvalho Chehab 	u32 VCAarg;
639a0bf528SMauro Carvalho Chehab 	u32 VGAarg;
649a0bf528SMauro Carvalho Chehab 	u32 bandselectarg;
659a0bf528SMauro Carvalho Chehab 	u32 pllarg;
669a0bf528SMauro Carvalho Chehab 	u32 FILTune;
679a0bf528SMauro Carvalho Chehab 
689a0bf528SMauro Carvalho Chehab 	struct i2c_adapter tuner_i2c_adapter;
699a0bf528SMauro Carvalho Chehab 
709a0bf528SMauro Carvalho Chehab 	u8 demod_rev;
719a0bf528SMauro Carvalho Chehab 
729a0bf528SMauro Carvalho Chehab 	/* The Demod/Tuner can't easily provide these, we cache them */
739a0bf528SMauro Carvalho Chehab 	u32 currentfreq;
749a0bf528SMauro Carvalho Chehab 	u32 currentsymbolrate;
759a0bf528SMauro Carvalho Chehab };
769a0bf528SMauro Carvalho Chehab 
779a0bf528SMauro Carvalho Chehab /* Various tuner defaults need to be established for a given symbol rate Sps */
789a0bf528SMauro Carvalho Chehab static struct cx24123_AGC_val {
799a0bf528SMauro Carvalho Chehab 	u32 symbolrate_low;
809a0bf528SMauro Carvalho Chehab 	u32 symbolrate_high;
819a0bf528SMauro Carvalho Chehab 	u32 VCAprogdata;
829a0bf528SMauro Carvalho Chehab 	u32 VGAprogdata;
839a0bf528SMauro Carvalho Chehab 	u32 FILTune;
849a0bf528SMauro Carvalho Chehab } cx24123_AGC_vals[] =
859a0bf528SMauro Carvalho Chehab {
869a0bf528SMauro Carvalho Chehab 	{
879a0bf528SMauro Carvalho Chehab 		.symbolrate_low		= 1000000,
889a0bf528SMauro Carvalho Chehab 		.symbolrate_high	= 4999999,
899a0bf528SMauro Carvalho Chehab 		/* the specs recommend other values for VGA offsets,
909a0bf528SMauro Carvalho Chehab 		   but tests show they are wrong */
919a0bf528SMauro Carvalho Chehab 		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
929a0bf528SMauro Carvalho Chehab 		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x07,
939a0bf528SMauro Carvalho Chehab 		.FILTune		= 0x27f /* 0.41 V */
949a0bf528SMauro Carvalho Chehab 	},
959a0bf528SMauro Carvalho Chehab 	{
969a0bf528SMauro Carvalho Chehab 		.symbolrate_low		=  5000000,
979a0bf528SMauro Carvalho Chehab 		.symbolrate_high	= 14999999,
989a0bf528SMauro Carvalho Chehab 		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
999a0bf528SMauro Carvalho Chehab 		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x1f,
1009a0bf528SMauro Carvalho Chehab 		.FILTune		= 0x317 /* 0.90 V */
1019a0bf528SMauro Carvalho Chehab 	},
1029a0bf528SMauro Carvalho Chehab 	{
1039a0bf528SMauro Carvalho Chehab 		.symbolrate_low		= 15000000,
1049a0bf528SMauro Carvalho Chehab 		.symbolrate_high	= 45000000,
1059a0bf528SMauro Carvalho Chehab 		.VGAprogdata		= (1 << 19) | (0x100 << 9) | 0x180,
1069a0bf528SMauro Carvalho Chehab 		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x3f,
1079a0bf528SMauro Carvalho Chehab 		.FILTune		= 0x145 /* 2.70 V */
1089a0bf528SMauro Carvalho Chehab 	},
1099a0bf528SMauro Carvalho Chehab };
1109a0bf528SMauro Carvalho Chehab 
1119a0bf528SMauro Carvalho Chehab /*
1129a0bf528SMauro Carvalho Chehab  * Various tuner defaults need to be established for a given frequency kHz.
1139a0bf528SMauro Carvalho Chehab  * fixme: The bounds on the bands do not match the doc in real life.
1149a0bf528SMauro Carvalho Chehab  * fixme: Some of them have been moved, other might need adjustment.
1159a0bf528SMauro Carvalho Chehab  */
1169a0bf528SMauro Carvalho Chehab static struct cx24123_bandselect_val {
1179a0bf528SMauro Carvalho Chehab 	u32 freq_low;
1189a0bf528SMauro Carvalho Chehab 	u32 freq_high;
1199a0bf528SMauro Carvalho Chehab 	u32 VCOdivider;
1209a0bf528SMauro Carvalho Chehab 	u32 progdata;
1219a0bf528SMauro Carvalho Chehab } cx24123_bandselect_vals[] =
1229a0bf528SMauro Carvalho Chehab {
1239a0bf528SMauro Carvalho Chehab 	/* band 1 */
1249a0bf528SMauro Carvalho Chehab 	{
1259a0bf528SMauro Carvalho Chehab 		.freq_low	= 950000,
1269a0bf528SMauro Carvalho Chehab 		.freq_high	= 1074999,
1279a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 4,
1289a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (0 << 9) | 0x40,
1299a0bf528SMauro Carvalho Chehab 	},
1309a0bf528SMauro Carvalho Chehab 
1319a0bf528SMauro Carvalho Chehab 	/* band 2 */
1329a0bf528SMauro Carvalho Chehab 	{
1339a0bf528SMauro Carvalho Chehab 		.freq_low	= 1075000,
1349a0bf528SMauro Carvalho Chehab 		.freq_high	= 1177999,
1359a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 4,
1369a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (0 << 9) | 0x80,
1379a0bf528SMauro Carvalho Chehab 	},
1389a0bf528SMauro Carvalho Chehab 
1399a0bf528SMauro Carvalho Chehab 	/* band 3 */
1409a0bf528SMauro Carvalho Chehab 	{
1419a0bf528SMauro Carvalho Chehab 		.freq_low	= 1178000,
1429a0bf528SMauro Carvalho Chehab 		.freq_high	= 1295999,
1439a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1449a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x01,
1459a0bf528SMauro Carvalho Chehab 	},
1469a0bf528SMauro Carvalho Chehab 
1479a0bf528SMauro Carvalho Chehab 	/* band 4 */
1489a0bf528SMauro Carvalho Chehab 	{
1499a0bf528SMauro Carvalho Chehab 		.freq_low	= 1296000,
1509a0bf528SMauro Carvalho Chehab 		.freq_high	= 1431999,
1519a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1529a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x02,
1539a0bf528SMauro Carvalho Chehab 	},
1549a0bf528SMauro Carvalho Chehab 
1559a0bf528SMauro Carvalho Chehab 	/* band 5 */
1569a0bf528SMauro Carvalho Chehab 	{
1579a0bf528SMauro Carvalho Chehab 		.freq_low	= 1432000,
1589a0bf528SMauro Carvalho Chehab 		.freq_high	= 1575999,
1599a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1609a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x04,
1619a0bf528SMauro Carvalho Chehab 	},
1629a0bf528SMauro Carvalho Chehab 
1639a0bf528SMauro Carvalho Chehab 	/* band 6 */
1649a0bf528SMauro Carvalho Chehab 	{
1659a0bf528SMauro Carvalho Chehab 		.freq_low	= 1576000,
1669a0bf528SMauro Carvalho Chehab 		.freq_high	= 1717999,
1679a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1689a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x08,
1699a0bf528SMauro Carvalho Chehab 	},
1709a0bf528SMauro Carvalho Chehab 
1719a0bf528SMauro Carvalho Chehab 	/* band 7 */
1729a0bf528SMauro Carvalho Chehab 	{
1739a0bf528SMauro Carvalho Chehab 		.freq_low	= 1718000,
1749a0bf528SMauro Carvalho Chehab 		.freq_high	= 1855999,
1759a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1769a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x10,
1779a0bf528SMauro Carvalho Chehab 	},
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	/* band 8 */
1809a0bf528SMauro Carvalho Chehab 	{
1819a0bf528SMauro Carvalho Chehab 		.freq_low	= 1856000,
1829a0bf528SMauro Carvalho Chehab 		.freq_high	= 2035999,
1839a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1849a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x20,
1859a0bf528SMauro Carvalho Chehab 	},
1869a0bf528SMauro Carvalho Chehab 
1879a0bf528SMauro Carvalho Chehab 	/* band 9 */
1889a0bf528SMauro Carvalho Chehab 	{
1899a0bf528SMauro Carvalho Chehab 		.freq_low	= 2036000,
1909a0bf528SMauro Carvalho Chehab 		.freq_high	= 2150000,
1919a0bf528SMauro Carvalho Chehab 		.VCOdivider	= 2,
1929a0bf528SMauro Carvalho Chehab 		.progdata	= (0 << 19) | (1 << 9) | 0x40,
1939a0bf528SMauro Carvalho Chehab 	},
1949a0bf528SMauro Carvalho Chehab };
1959a0bf528SMauro Carvalho Chehab 
1969a0bf528SMauro Carvalho Chehab static struct {
1979a0bf528SMauro Carvalho Chehab 	u8 reg;
1989a0bf528SMauro Carvalho Chehab 	u8 data;
1999a0bf528SMauro Carvalho Chehab } cx24123_regdata[] =
2009a0bf528SMauro Carvalho Chehab {
2019a0bf528SMauro Carvalho Chehab 	{0x00, 0x03}, /* Reset system */
2029a0bf528SMauro Carvalho Chehab 	{0x00, 0x00}, /* Clear reset */
2039a0bf528SMauro Carvalho Chehab 	{0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */
2049a0bf528SMauro Carvalho Chehab 	{0x04, 0x10}, /* MPEG */
2059a0bf528SMauro Carvalho Chehab 	{0x05, 0x04}, /* MPEG */
2069a0bf528SMauro Carvalho Chehab 	{0x06, 0x31}, /* MPEG (default) */
2079a0bf528SMauro Carvalho Chehab 	{0x0b, 0x00}, /* Freq search start point (default) */
2089a0bf528SMauro Carvalho Chehab 	{0x0c, 0x00}, /* Demodulator sample gain (default) */
2099a0bf528SMauro Carvalho Chehab 	{0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */
2109a0bf528SMauro Carvalho Chehab 	{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
2119a0bf528SMauro Carvalho Chehab 	{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
2129a0bf528SMauro Carvalho Chehab 	{0x10, 0x01}, /* Default search inversion, no repeat (default) */
2139a0bf528SMauro Carvalho Chehab 	{0x16, 0x00}, /* Enable reading of frequency */
2149a0bf528SMauro Carvalho Chehab 	{0x17, 0x01}, /* Enable EsNO Ready Counter */
2159a0bf528SMauro Carvalho Chehab 	{0x1c, 0x80}, /* Enable error counter */
2169a0bf528SMauro Carvalho Chehab 	{0x20, 0x00}, /* Tuner burst clock rate = 500KHz */
2179a0bf528SMauro Carvalho Chehab 	{0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */
2189a0bf528SMauro Carvalho Chehab 	{0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */
2199a0bf528SMauro Carvalho Chehab 	{0x29, 0x00}, /* DiSEqC LNB_DC off */
2209a0bf528SMauro Carvalho Chehab 	{0x2a, 0xb0}, /* DiSEqC Parameters (default) */
2219a0bf528SMauro Carvalho Chehab 	{0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */
2229a0bf528SMauro Carvalho Chehab 	{0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */
2239a0bf528SMauro Carvalho Chehab 	{0x2d, 0x00},
2249a0bf528SMauro Carvalho Chehab 	{0x2e, 0x00},
2259a0bf528SMauro Carvalho Chehab 	{0x2f, 0x00},
2269a0bf528SMauro Carvalho Chehab 	{0x30, 0x00},
2279a0bf528SMauro Carvalho Chehab 	{0x31, 0x00},
2289a0bf528SMauro Carvalho Chehab 	{0x32, 0x8c}, /* DiSEqC Parameters (default) */
2299a0bf528SMauro Carvalho Chehab 	{0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */
2309a0bf528SMauro Carvalho Chehab 	{0x34, 0x00},
2319a0bf528SMauro Carvalho Chehab 	{0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */
2329a0bf528SMauro Carvalho Chehab 	{0x36, 0x02}, /* DiSEqC Parameters (default) */
2339a0bf528SMauro Carvalho Chehab 	{0x37, 0x3a}, /* DiSEqC Parameters (default) */
2349a0bf528SMauro Carvalho Chehab 	{0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */
2359a0bf528SMauro Carvalho Chehab 	{0x44, 0x00}, /* Constellation (default) */
2369a0bf528SMauro Carvalho Chehab 	{0x45, 0x00}, /* Symbol count (default) */
2379a0bf528SMauro Carvalho Chehab 	{0x46, 0x0d}, /* Symbol rate estimator on (default) */
2389a0bf528SMauro Carvalho Chehab 	{0x56, 0xc1}, /* Error Counter = Viterbi BER */
2399a0bf528SMauro Carvalho Chehab 	{0x57, 0xff}, /* Error Counter Window (default) */
2409a0bf528SMauro Carvalho Chehab 	{0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */
2419a0bf528SMauro Carvalho Chehab 	{0x67, 0x83}, /* Non-DCII symbol clock */
2429a0bf528SMauro Carvalho Chehab };
2439a0bf528SMauro Carvalho Chehab 
2449a0bf528SMauro Carvalho Chehab static int cx24123_i2c_writereg(struct cx24123_state *state,
2459a0bf528SMauro Carvalho Chehab 	u8 i2c_addr, int reg, int data)
2469a0bf528SMauro Carvalho Chehab {
2479a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
2489a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
2499a0bf528SMauro Carvalho Chehab 		.addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
2509a0bf528SMauro Carvalho Chehab 	};
2519a0bf528SMauro Carvalho Chehab 	int err;
2529a0bf528SMauro Carvalho Chehab 
2539a0bf528SMauro Carvalho Chehab 	/* printk(KERN_DEBUG "wr(%02x): %02x %02x\n", i2c_addr, reg, data); */
2549a0bf528SMauro Carvalho Chehab 
2559a0bf528SMauro Carvalho Chehab 	err = i2c_transfer(state->i2c, &msg, 1);
2569a0bf528SMauro Carvalho Chehab 	if (err != 1) {
2579a0bf528SMauro Carvalho Chehab 		printk("%s: writereg error(err == %i, reg == 0x%02x,"
2589a0bf528SMauro Carvalho Chehab 			 " data == 0x%02x)\n", __func__, err, reg, data);
2599a0bf528SMauro Carvalho Chehab 		return err;
2609a0bf528SMauro Carvalho Chehab 	}
2619a0bf528SMauro Carvalho Chehab 
2629a0bf528SMauro Carvalho Chehab 	return 0;
2639a0bf528SMauro Carvalho Chehab }
2649a0bf528SMauro Carvalho Chehab 
2659a0bf528SMauro Carvalho Chehab static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg)
2669a0bf528SMauro Carvalho Chehab {
2679a0bf528SMauro Carvalho Chehab 	int ret;
2689a0bf528SMauro Carvalho Chehab 	u8 b = 0;
2699a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
2709a0bf528SMauro Carvalho Chehab 		{ .addr = i2c_addr, .flags = 0, .buf = &reg, .len = 1 },
2719a0bf528SMauro Carvalho Chehab 		{ .addr = i2c_addr, .flags = I2C_M_RD, .buf = &b, .len = 1 }
2729a0bf528SMauro Carvalho Chehab 	};
2739a0bf528SMauro Carvalho Chehab 
2749a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	if (ret != 2) {
2779a0bf528SMauro Carvalho Chehab 		err("%s: reg=0x%x (error=%d)\n", __func__, reg, ret);
2789a0bf528SMauro Carvalho Chehab 		return ret;
2799a0bf528SMauro Carvalho Chehab 	}
2809a0bf528SMauro Carvalho Chehab 
2819a0bf528SMauro Carvalho Chehab 	/* printk(KERN_DEBUG "rd(%02x): %02x %02x\n", i2c_addr, reg, b); */
2829a0bf528SMauro Carvalho Chehab 
2839a0bf528SMauro Carvalho Chehab 	return b;
2849a0bf528SMauro Carvalho Chehab }
2859a0bf528SMauro Carvalho Chehab 
2869a0bf528SMauro Carvalho Chehab #define cx24123_readreg(state, reg) \
2879a0bf528SMauro Carvalho Chehab 	cx24123_i2c_readreg(state, state->config->demod_address, reg)
2889a0bf528SMauro Carvalho Chehab #define cx24123_writereg(state, reg, val) \
2899a0bf528SMauro Carvalho Chehab 	cx24123_i2c_writereg(state, state->config->demod_address, reg, val)
2909a0bf528SMauro Carvalho Chehab 
2919a0bf528SMauro Carvalho Chehab static int cx24123_set_inversion(struct cx24123_state *state,
2929a0bf528SMauro Carvalho Chehab 	fe_spectral_inversion_t inversion)
2939a0bf528SMauro Carvalho Chehab {
2949a0bf528SMauro Carvalho Chehab 	u8 nom_reg = cx24123_readreg(state, 0x0e);
2959a0bf528SMauro Carvalho Chehab 	u8 auto_reg = cx24123_readreg(state, 0x10);
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab 	switch (inversion) {
2989a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
2999a0bf528SMauro Carvalho Chehab 		dprintk("inversion off\n");
3009a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg & ~0x80);
3019a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x10, auto_reg | 0x80);
3029a0bf528SMauro Carvalho Chehab 		break;
3039a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
3049a0bf528SMauro Carvalho Chehab 		dprintk("inversion on\n");
3059a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x80);
3069a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x10, auto_reg | 0x80);
3079a0bf528SMauro Carvalho Chehab 		break;
3089a0bf528SMauro Carvalho Chehab 	case INVERSION_AUTO:
3099a0bf528SMauro Carvalho Chehab 		dprintk("inversion auto\n");
3109a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x10, auto_reg & ~0x80);
3119a0bf528SMauro Carvalho Chehab 		break;
3129a0bf528SMauro Carvalho Chehab 	default:
3139a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3149a0bf528SMauro Carvalho Chehab 	}
3159a0bf528SMauro Carvalho Chehab 
3169a0bf528SMauro Carvalho Chehab 	return 0;
3179a0bf528SMauro Carvalho Chehab }
3189a0bf528SMauro Carvalho Chehab 
3199a0bf528SMauro Carvalho Chehab static int cx24123_get_inversion(struct cx24123_state *state,
3209a0bf528SMauro Carvalho Chehab 	fe_spectral_inversion_t *inversion)
3219a0bf528SMauro Carvalho Chehab {
3229a0bf528SMauro Carvalho Chehab 	u8 val;
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x1b) >> 7;
3259a0bf528SMauro Carvalho Chehab 
3269a0bf528SMauro Carvalho Chehab 	if (val == 0) {
3279a0bf528SMauro Carvalho Chehab 		dprintk("read inversion off\n");
3289a0bf528SMauro Carvalho Chehab 		*inversion = INVERSION_OFF;
3299a0bf528SMauro Carvalho Chehab 	} else {
3309a0bf528SMauro Carvalho Chehab 		dprintk("read inversion on\n");
3319a0bf528SMauro Carvalho Chehab 		*inversion = INVERSION_ON;
3329a0bf528SMauro Carvalho Chehab 	}
3339a0bf528SMauro Carvalho Chehab 
3349a0bf528SMauro Carvalho Chehab 	return 0;
3359a0bf528SMauro Carvalho Chehab }
3369a0bf528SMauro Carvalho Chehab 
3379a0bf528SMauro Carvalho Chehab static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec)
3389a0bf528SMauro Carvalho Chehab {
3399a0bf528SMauro Carvalho Chehab 	u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07;
3409a0bf528SMauro Carvalho Chehab 
3419a0bf528SMauro Carvalho Chehab 	if ((fec < FEC_NONE) || (fec > FEC_AUTO))
3429a0bf528SMauro Carvalho Chehab 		fec = FEC_AUTO;
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 	/* Set the soft decision threshold */
3459a0bf528SMauro Carvalho Chehab 	if (fec == FEC_1_2)
3469a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x43,
3479a0bf528SMauro Carvalho Chehab 			cx24123_readreg(state, 0x43) | 0x01);
3489a0bf528SMauro Carvalho Chehab 	else
3499a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x43,
3509a0bf528SMauro Carvalho Chehab 			cx24123_readreg(state, 0x43) & ~0x01);
3519a0bf528SMauro Carvalho Chehab 
3529a0bf528SMauro Carvalho Chehab 	switch (fec) {
3539a0bf528SMauro Carvalho Chehab 	case FEC_1_2:
3549a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 1/2\n");
3559a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x01);
3569a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x02);
3579a0bf528SMauro Carvalho Chehab 		break;
3589a0bf528SMauro Carvalho Chehab 	case FEC_2_3:
3599a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 2/3\n");
3609a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x02);
3619a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x04);
3629a0bf528SMauro Carvalho Chehab 		break;
3639a0bf528SMauro Carvalho Chehab 	case FEC_3_4:
3649a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 3/4\n");
3659a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x03);
3669a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x08);
3679a0bf528SMauro Carvalho Chehab 		break;
3689a0bf528SMauro Carvalho Chehab 	case FEC_4_5:
3699a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 4/5\n");
3709a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x04);
3719a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x10);
3729a0bf528SMauro Carvalho Chehab 		break;
3739a0bf528SMauro Carvalho Chehab 	case FEC_5_6:
3749a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 5/6\n");
3759a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x05);
3769a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x20);
3779a0bf528SMauro Carvalho Chehab 		break;
3789a0bf528SMauro Carvalho Chehab 	case FEC_6_7:
3799a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 6/7\n");
3809a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x06);
3819a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x40);
3829a0bf528SMauro Carvalho Chehab 		break;
3839a0bf528SMauro Carvalho Chehab 	case FEC_7_8:
3849a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to 7/8\n");
3859a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0e, nom_reg | 0x07);
3869a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0x80);
3879a0bf528SMauro Carvalho Chehab 		break;
3889a0bf528SMauro Carvalho Chehab 	case FEC_AUTO:
3899a0bf528SMauro Carvalho Chehab 		dprintk("set FEC to auto\n");
3909a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x0f, 0xfe);
3919a0bf528SMauro Carvalho Chehab 		break;
3929a0bf528SMauro Carvalho Chehab 	default:
3939a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
3949a0bf528SMauro Carvalho Chehab 	}
3959a0bf528SMauro Carvalho Chehab 
3969a0bf528SMauro Carvalho Chehab 	return 0;
3979a0bf528SMauro Carvalho Chehab }
3989a0bf528SMauro Carvalho Chehab 
3999a0bf528SMauro Carvalho Chehab static int cx24123_get_fec(struct cx24123_state *state, fe_code_rate_t *fec)
4009a0bf528SMauro Carvalho Chehab {
4019a0bf528SMauro Carvalho Chehab 	int ret;
4029a0bf528SMauro Carvalho Chehab 
4039a0bf528SMauro Carvalho Chehab 	ret = cx24123_readreg(state, 0x1b);
4049a0bf528SMauro Carvalho Chehab 	if (ret < 0)
4059a0bf528SMauro Carvalho Chehab 		return ret;
4069a0bf528SMauro Carvalho Chehab 	ret = ret & 0x07;
4079a0bf528SMauro Carvalho Chehab 
4089a0bf528SMauro Carvalho Chehab 	switch (ret) {
4099a0bf528SMauro Carvalho Chehab 	case 1:
4109a0bf528SMauro Carvalho Chehab 		*fec = FEC_1_2;
4119a0bf528SMauro Carvalho Chehab 		break;
4129a0bf528SMauro Carvalho Chehab 	case 2:
4139a0bf528SMauro Carvalho Chehab 		*fec = FEC_2_3;
4149a0bf528SMauro Carvalho Chehab 		break;
4159a0bf528SMauro Carvalho Chehab 	case 3:
4169a0bf528SMauro Carvalho Chehab 		*fec = FEC_3_4;
4179a0bf528SMauro Carvalho Chehab 		break;
4189a0bf528SMauro Carvalho Chehab 	case 4:
4199a0bf528SMauro Carvalho Chehab 		*fec = FEC_4_5;
4209a0bf528SMauro Carvalho Chehab 		break;
4219a0bf528SMauro Carvalho Chehab 	case 5:
4229a0bf528SMauro Carvalho Chehab 		*fec = FEC_5_6;
4239a0bf528SMauro Carvalho Chehab 		break;
4249a0bf528SMauro Carvalho Chehab 	case 6:
4259a0bf528SMauro Carvalho Chehab 		*fec = FEC_6_7;
4269a0bf528SMauro Carvalho Chehab 		break;
4279a0bf528SMauro Carvalho Chehab 	case 7:
4289a0bf528SMauro Carvalho Chehab 		*fec = FEC_7_8;
4299a0bf528SMauro Carvalho Chehab 		break;
4309a0bf528SMauro Carvalho Chehab 	default:
4319a0bf528SMauro Carvalho Chehab 		/* this can happen when there's no lock */
4329a0bf528SMauro Carvalho Chehab 		*fec = FEC_NONE;
4339a0bf528SMauro Carvalho Chehab 	}
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 	return 0;
4369a0bf528SMauro Carvalho Chehab }
4379a0bf528SMauro Carvalho Chehab 
4389a0bf528SMauro Carvalho Chehab /* Approximation of closest integer of log2(a/b). It actually gives the
4399a0bf528SMauro Carvalho Chehab    lowest integer i such that 2^i >= round(a/b) */
4409a0bf528SMauro Carvalho Chehab static u32 cx24123_int_log2(u32 a, u32 b)
4419a0bf528SMauro Carvalho Chehab {
4429a0bf528SMauro Carvalho Chehab 	u32 exp, nearest = 0;
4439a0bf528SMauro Carvalho Chehab 	u32 div = a / b;
4449a0bf528SMauro Carvalho Chehab 	if (a % b >= b / 2)
4459a0bf528SMauro Carvalho Chehab 		++div;
4469a0bf528SMauro Carvalho Chehab 	if (div < (1 << 31)) {
4479a0bf528SMauro Carvalho Chehab 		for (exp = 1; div > exp; nearest++)
4489a0bf528SMauro Carvalho Chehab 			exp += exp;
4499a0bf528SMauro Carvalho Chehab 	}
4509a0bf528SMauro Carvalho Chehab 	return nearest;
4519a0bf528SMauro Carvalho Chehab }
4529a0bf528SMauro Carvalho Chehab 
4539a0bf528SMauro Carvalho Chehab static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate)
4549a0bf528SMauro Carvalho Chehab {
4559a0bf528SMauro Carvalho Chehab 	u32 tmp, sample_rate, ratio, sample_gain;
4569a0bf528SMauro Carvalho Chehab 	u8 pll_mult;
4579a0bf528SMauro Carvalho Chehab 
4589a0bf528SMauro Carvalho Chehab 	/*  check if symbol rate is within limits */
4599a0bf528SMauro Carvalho Chehab 	if ((srate > state->frontend.ops.info.symbol_rate_max) ||
4609a0bf528SMauro Carvalho Chehab 	    (srate < state->frontend.ops.info.symbol_rate_min))
4619a0bf528SMauro Carvalho Chehab 		return -EOPNOTSUPP;
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 	/* choose the sampling rate high enough for the required operation,
4649a0bf528SMauro Carvalho Chehab 	   while optimizing the power consumed by the demodulator */
4659a0bf528SMauro Carvalho Chehab 	if (srate < (XTAL*2)/2)
4669a0bf528SMauro Carvalho Chehab 		pll_mult = 2;
4679a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*3)/2)
4689a0bf528SMauro Carvalho Chehab 		pll_mult = 3;
4699a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*4)/2)
4709a0bf528SMauro Carvalho Chehab 		pll_mult = 4;
4719a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*5)/2)
4729a0bf528SMauro Carvalho Chehab 		pll_mult = 5;
4739a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*6)/2)
4749a0bf528SMauro Carvalho Chehab 		pll_mult = 6;
4759a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*7)/2)
4769a0bf528SMauro Carvalho Chehab 		pll_mult = 7;
4779a0bf528SMauro Carvalho Chehab 	else if (srate < (XTAL*8)/2)
4789a0bf528SMauro Carvalho Chehab 		pll_mult = 8;
4799a0bf528SMauro Carvalho Chehab 	else
4809a0bf528SMauro Carvalho Chehab 		pll_mult = 9;
4819a0bf528SMauro Carvalho Chehab 
4829a0bf528SMauro Carvalho Chehab 
4839a0bf528SMauro Carvalho Chehab 	sample_rate = pll_mult * XTAL;
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 	/*
4869a0bf528SMauro Carvalho Chehab 	    SYSSymbolRate[21:0] = (srate << 23) / sample_rate
4879a0bf528SMauro Carvalho Chehab 
4889a0bf528SMauro Carvalho Chehab 	    We have to use 32 bit unsigned arithmetic without precision loss.
4899a0bf528SMauro Carvalho Chehab 	    The maximum srate is 45000000 or 0x02AEA540. This number has
4909a0bf528SMauro Carvalho Chehab 	    only 6 clear bits on top, hence we can shift it left only 6 bits
4919a0bf528SMauro Carvalho Chehab 	    at a time. Borrowed from cx24110.c
4929a0bf528SMauro Carvalho Chehab 	*/
4939a0bf528SMauro Carvalho Chehab 
4949a0bf528SMauro Carvalho Chehab 	tmp = srate << 6;
4959a0bf528SMauro Carvalho Chehab 	ratio = tmp / sample_rate;
4969a0bf528SMauro Carvalho Chehab 
4979a0bf528SMauro Carvalho Chehab 	tmp = (tmp % sample_rate) << 6;
4989a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 6) + (tmp / sample_rate);
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 	tmp = (tmp % sample_rate) << 6;
5019a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 6) + (tmp / sample_rate);
5029a0bf528SMauro Carvalho Chehab 
5039a0bf528SMauro Carvalho Chehab 	tmp = (tmp % sample_rate) << 5;
5049a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 5) + (tmp / sample_rate);
5059a0bf528SMauro Carvalho Chehab 
5069a0bf528SMauro Carvalho Chehab 
5079a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x01, pll_mult * 6);
5089a0bf528SMauro Carvalho Chehab 
5099a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f);
5109a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff);
5119a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x0a, ratio & 0xff);
5129a0bf528SMauro Carvalho Chehab 
5139a0bf528SMauro Carvalho Chehab 	/* also set the demodulator sample gain */
5149a0bf528SMauro Carvalho Chehab 	sample_gain = cx24123_int_log2(sample_rate, srate);
5159a0bf528SMauro Carvalho Chehab 	tmp = cx24123_readreg(state, 0x0c) & ~0xe0;
5169a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x0c, tmp | sample_gain << 5);
5179a0bf528SMauro Carvalho Chehab 
5189a0bf528SMauro Carvalho Chehab 	dprintk("srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n",
5199a0bf528SMauro Carvalho Chehab 		srate, ratio, sample_rate, sample_gain);
5209a0bf528SMauro Carvalho Chehab 
5219a0bf528SMauro Carvalho Chehab 	return 0;
5229a0bf528SMauro Carvalho Chehab }
5239a0bf528SMauro Carvalho Chehab 
5249a0bf528SMauro Carvalho Chehab /*
5259a0bf528SMauro Carvalho Chehab  * Based on the required frequency and symbolrate, the tuner AGC has
5269a0bf528SMauro Carvalho Chehab  * to be configured and the correct band selected.
5279a0bf528SMauro Carvalho Chehab  * Calculate those values.
5289a0bf528SMauro Carvalho Chehab  */
5299a0bf528SMauro Carvalho Chehab static int cx24123_pll_calculate(struct dvb_frontend *fe)
5309a0bf528SMauro Carvalho Chehab {
5319a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
5329a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
5339a0bf528SMauro Carvalho Chehab 	u32 ndiv = 0, adiv = 0, vco_div = 0;
5349a0bf528SMauro Carvalho Chehab 	int i = 0;
5359a0bf528SMauro Carvalho Chehab 	int pump = 2;
5369a0bf528SMauro Carvalho Chehab 	int band = 0;
5379a0bf528SMauro Carvalho Chehab 	int num_bands = ARRAY_SIZE(cx24123_bandselect_vals);
5389a0bf528SMauro Carvalho Chehab 	struct cx24123_bandselect_val *bsv = NULL;
5399a0bf528SMauro Carvalho Chehab 	struct cx24123_AGC_val *agcv = NULL;
5409a0bf528SMauro Carvalho Chehab 
5419a0bf528SMauro Carvalho Chehab 	/* Defaults for low freq, low rate */
5429a0bf528SMauro Carvalho Chehab 	state->VCAarg = cx24123_AGC_vals[0].VCAprogdata;
5439a0bf528SMauro Carvalho Chehab 	state->VGAarg = cx24123_AGC_vals[0].VGAprogdata;
5449a0bf528SMauro Carvalho Chehab 	state->bandselectarg = cx24123_bandselect_vals[0].progdata;
5459a0bf528SMauro Carvalho Chehab 	vco_div = cx24123_bandselect_vals[0].VCOdivider;
5469a0bf528SMauro Carvalho Chehab 
5479a0bf528SMauro Carvalho Chehab 	/* For the given symbol rate, determine the VCA, VGA and
5489a0bf528SMauro Carvalho Chehab 	 * FILTUNE programming bits */
5499a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) {
5509a0bf528SMauro Carvalho Chehab 		agcv = &cx24123_AGC_vals[i];
5519a0bf528SMauro Carvalho Chehab 		if ((agcv->symbolrate_low <= p->symbol_rate) &&
5529a0bf528SMauro Carvalho Chehab 		    (agcv->symbolrate_high >= p->symbol_rate)) {
5539a0bf528SMauro Carvalho Chehab 			state->VCAarg = agcv->VCAprogdata;
5549a0bf528SMauro Carvalho Chehab 			state->VGAarg = agcv->VGAprogdata;
5559a0bf528SMauro Carvalho Chehab 			state->FILTune = agcv->FILTune;
5569a0bf528SMauro Carvalho Chehab 		}
5579a0bf528SMauro Carvalho Chehab 	}
5589a0bf528SMauro Carvalho Chehab 
5599a0bf528SMauro Carvalho Chehab 	/* determine the band to use */
5609a0bf528SMauro Carvalho Chehab 	if (force_band < 1 || force_band > num_bands) {
5619a0bf528SMauro Carvalho Chehab 		for (i = 0; i < num_bands; i++) {
5629a0bf528SMauro Carvalho Chehab 			bsv = &cx24123_bandselect_vals[i];
5639a0bf528SMauro Carvalho Chehab 			if ((bsv->freq_low <= p->frequency) &&
5649a0bf528SMauro Carvalho Chehab 				(bsv->freq_high >= p->frequency))
5659a0bf528SMauro Carvalho Chehab 				band = i;
5669a0bf528SMauro Carvalho Chehab 		}
5679a0bf528SMauro Carvalho Chehab 	} else
5689a0bf528SMauro Carvalho Chehab 		band = force_band - 1;
5699a0bf528SMauro Carvalho Chehab 
5709a0bf528SMauro Carvalho Chehab 	state->bandselectarg = cx24123_bandselect_vals[band].progdata;
5719a0bf528SMauro Carvalho Chehab 	vco_div = cx24123_bandselect_vals[band].VCOdivider;
5729a0bf528SMauro Carvalho Chehab 
5739a0bf528SMauro Carvalho Chehab 	/* determine the charge pump current */
5749a0bf528SMauro Carvalho Chehab 	if (p->frequency < (cx24123_bandselect_vals[band].freq_low +
5759a0bf528SMauro Carvalho Chehab 		cx24123_bandselect_vals[band].freq_high) / 2)
5769a0bf528SMauro Carvalho Chehab 		pump = 0x01;
5779a0bf528SMauro Carvalho Chehab 	else
5789a0bf528SMauro Carvalho Chehab 		pump = 0x02;
5799a0bf528SMauro Carvalho Chehab 
5809a0bf528SMauro Carvalho Chehab 	/* Determine the N/A dividers for the requested lband freq (in kHz). */
5819a0bf528SMauro Carvalho Chehab 	/* Note: the reference divider R=10, frequency is in KHz,
5829a0bf528SMauro Carvalho Chehab 	 * XTAL is in Hz */
5839a0bf528SMauro Carvalho Chehab 	ndiv = (((p->frequency * vco_div * 10) /
5849a0bf528SMauro Carvalho Chehab 		(2 * XTAL / 1000)) / 32) & 0x1ff;
5859a0bf528SMauro Carvalho Chehab 	adiv = (((p->frequency * vco_div * 10) /
5869a0bf528SMauro Carvalho Chehab 		(2 * XTAL / 1000)) % 32) & 0x1f;
5879a0bf528SMauro Carvalho Chehab 
5889a0bf528SMauro Carvalho Chehab 	if (adiv == 0 && ndiv > 0)
5899a0bf528SMauro Carvalho Chehab 		ndiv--;
5909a0bf528SMauro Carvalho Chehab 
5919a0bf528SMauro Carvalho Chehab 	/* control bits 11, refdiv 11, charge pump polarity 1,
5929a0bf528SMauro Carvalho Chehab 	 * charge pump current, ndiv, adiv */
5939a0bf528SMauro Carvalho Chehab 	state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) |
5949a0bf528SMauro Carvalho Chehab 		(pump << 14) | (ndiv << 5) | adiv;
5959a0bf528SMauro Carvalho Chehab 
5969a0bf528SMauro Carvalho Chehab 	return 0;
5979a0bf528SMauro Carvalho Chehab }
5989a0bf528SMauro Carvalho Chehab 
5999a0bf528SMauro Carvalho Chehab /*
6009a0bf528SMauro Carvalho Chehab  * Tuner data is 21 bits long, must be left-aligned in data.
6019a0bf528SMauro Carvalho Chehab  * Tuner cx24109 is written through a dedicated 3wire interface
6029a0bf528SMauro Carvalho Chehab  * on the demod chip.
6039a0bf528SMauro Carvalho Chehab  */
6049a0bf528SMauro Carvalho Chehab static int cx24123_pll_writereg(struct dvb_frontend *fe, u32 data)
6059a0bf528SMauro Carvalho Chehab {
6069a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
6079a0bf528SMauro Carvalho Chehab 	unsigned long timeout;
6089a0bf528SMauro Carvalho Chehab 
6099a0bf528SMauro Carvalho Chehab 	dprintk("pll writereg called, data=0x%08x\n", data);
6109a0bf528SMauro Carvalho Chehab 
6119a0bf528SMauro Carvalho Chehab 	/* align the 21 bytes into to bit23 boundary */
6129a0bf528SMauro Carvalho Chehab 	data = data << 3;
6139a0bf528SMauro Carvalho Chehab 
6149a0bf528SMauro Carvalho Chehab 	/* Reset the demod pll word length to 0x15 bits */
6159a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x21, 0x15);
6169a0bf528SMauro Carvalho Chehab 
6179a0bf528SMauro Carvalho Chehab 	/* write the msb 8 bits, wait for the send to be completed */
6189a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(40);
6199a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x22, (data >> 16) & 0xff);
6209a0bf528SMauro Carvalho Chehab 	while ((cx24123_readreg(state, 0x20) & 0x40) == 0) {
6219a0bf528SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
6229a0bf528SMauro Carvalho Chehab 			err("%s:  demodulator is not responding, "\
6239a0bf528SMauro Carvalho Chehab 				"possibly hung, aborting.\n", __func__);
6249a0bf528SMauro Carvalho Chehab 			return -EREMOTEIO;
6259a0bf528SMauro Carvalho Chehab 		}
6269a0bf528SMauro Carvalho Chehab 		msleep(10);
6279a0bf528SMauro Carvalho Chehab 	}
6289a0bf528SMauro Carvalho Chehab 
6299a0bf528SMauro Carvalho Chehab 	/* send another 8 bytes, wait for the send to be completed */
6309a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(40);
6319a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x22, (data >> 8) & 0xff);
6329a0bf528SMauro Carvalho Chehab 	while ((cx24123_readreg(state, 0x20) & 0x40) == 0) {
6339a0bf528SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
6349a0bf528SMauro Carvalho Chehab 			err("%s:  demodulator is not responding, "\
6359a0bf528SMauro Carvalho Chehab 				"possibly hung, aborting.\n", __func__);
6369a0bf528SMauro Carvalho Chehab 			return -EREMOTEIO;
6379a0bf528SMauro Carvalho Chehab 		}
6389a0bf528SMauro Carvalho Chehab 		msleep(10);
6399a0bf528SMauro Carvalho Chehab 	}
6409a0bf528SMauro Carvalho Chehab 
6419a0bf528SMauro Carvalho Chehab 	/* send the lower 5 bits of this byte, padded with 3 LBB,
6429a0bf528SMauro Carvalho Chehab 	 * wait for the send to be completed */
6439a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(40);
6449a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x22, (data) & 0xff);
6459a0bf528SMauro Carvalho Chehab 	while ((cx24123_readreg(state, 0x20) & 0x80)) {
6469a0bf528SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
6479a0bf528SMauro Carvalho Chehab 			err("%s:  demodulator is not responding," \
6489a0bf528SMauro Carvalho Chehab 				"possibly hung, aborting.\n", __func__);
6499a0bf528SMauro Carvalho Chehab 			return -EREMOTEIO;
6509a0bf528SMauro Carvalho Chehab 		}
6519a0bf528SMauro Carvalho Chehab 		msleep(10);
6529a0bf528SMauro Carvalho Chehab 	}
6539a0bf528SMauro Carvalho Chehab 
6549a0bf528SMauro Carvalho Chehab 	/* Trigger the demod to configure the tuner */
6559a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2);
6569a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd);
6579a0bf528SMauro Carvalho Chehab 
6589a0bf528SMauro Carvalho Chehab 	return 0;
6599a0bf528SMauro Carvalho Chehab }
6609a0bf528SMauro Carvalho Chehab 
6619a0bf528SMauro Carvalho Chehab static int cx24123_pll_tune(struct dvb_frontend *fe)
6629a0bf528SMauro Carvalho Chehab {
6639a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
6649a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
6659a0bf528SMauro Carvalho Chehab 	u8 val;
6669a0bf528SMauro Carvalho Chehab 
6679a0bf528SMauro Carvalho Chehab 	dprintk("frequency=%i\n", p->frequency);
6689a0bf528SMauro Carvalho Chehab 
6699a0bf528SMauro Carvalho Chehab 	if (cx24123_pll_calculate(fe) != 0) {
6709a0bf528SMauro Carvalho Chehab 		err("%s: cx24123_pll_calcutate failed\n", __func__);
6719a0bf528SMauro Carvalho Chehab 		return -EINVAL;
6729a0bf528SMauro Carvalho Chehab 	}
6739a0bf528SMauro Carvalho Chehab 
6749a0bf528SMauro Carvalho Chehab 	/* Write the new VCO/VGA */
6759a0bf528SMauro Carvalho Chehab 	cx24123_pll_writereg(fe, state->VCAarg);
6769a0bf528SMauro Carvalho Chehab 	cx24123_pll_writereg(fe, state->VGAarg);
6779a0bf528SMauro Carvalho Chehab 
6789a0bf528SMauro Carvalho Chehab 	/* Write the new bandselect and pll args */
6799a0bf528SMauro Carvalho Chehab 	cx24123_pll_writereg(fe, state->bandselectarg);
6809a0bf528SMauro Carvalho Chehab 	cx24123_pll_writereg(fe, state->pllarg);
6819a0bf528SMauro Carvalho Chehab 
6829a0bf528SMauro Carvalho Chehab 	/* set the FILTUNE voltage */
6839a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x28) & ~0x3;
6849a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x27, state->FILTune >> 2);
6859a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3));
6869a0bf528SMauro Carvalho Chehab 
6879a0bf528SMauro Carvalho Chehab 	dprintk("pll tune VCA=%d, band=%d, pll=%d\n", state->VCAarg,
6889a0bf528SMauro Carvalho Chehab 			state->bandselectarg, state->pllarg);
6899a0bf528SMauro Carvalho Chehab 
6909a0bf528SMauro Carvalho Chehab 	return 0;
6919a0bf528SMauro Carvalho Chehab }
6929a0bf528SMauro Carvalho Chehab 
6939a0bf528SMauro Carvalho Chehab 
6949a0bf528SMauro Carvalho Chehab /*
6959a0bf528SMauro Carvalho Chehab  * 0x23:
6969a0bf528SMauro Carvalho Chehab  *    [7:7] = BTI enabled
6979a0bf528SMauro Carvalho Chehab  *    [6:6] = I2C repeater enabled
6989a0bf528SMauro Carvalho Chehab  *    [5:5] = I2C repeater start
6999a0bf528SMauro Carvalho Chehab  *    [0:0] = BTI start
7009a0bf528SMauro Carvalho Chehab  */
7019a0bf528SMauro Carvalho Chehab 
7029a0bf528SMauro Carvalho Chehab /* mode == 1 -> i2c-repeater, 0 -> bti */
7039a0bf528SMauro Carvalho Chehab static int cx24123_repeater_mode(struct cx24123_state *state, u8 mode, u8 start)
7049a0bf528SMauro Carvalho Chehab {
7059a0bf528SMauro Carvalho Chehab 	u8 r = cx24123_readreg(state, 0x23) & 0x1e;
7069a0bf528SMauro Carvalho Chehab 	if (mode)
7079a0bf528SMauro Carvalho Chehab 		r |= (1 << 6) | (start << 5);
7089a0bf528SMauro Carvalho Chehab 	else
7099a0bf528SMauro Carvalho Chehab 		r |= (1 << 7) | (start);
7109a0bf528SMauro Carvalho Chehab 	return cx24123_writereg(state, 0x23, r);
7119a0bf528SMauro Carvalho Chehab }
7129a0bf528SMauro Carvalho Chehab 
7139a0bf528SMauro Carvalho Chehab static int cx24123_initfe(struct dvb_frontend *fe)
7149a0bf528SMauro Carvalho Chehab {
7159a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
7169a0bf528SMauro Carvalho Chehab 	int i;
7179a0bf528SMauro Carvalho Chehab 
7189a0bf528SMauro Carvalho Chehab 	dprintk("init frontend\n");
7199a0bf528SMauro Carvalho Chehab 
7209a0bf528SMauro Carvalho Chehab 	/* Configure the demod to a good set of defaults */
7219a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++)
7229a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, cx24123_regdata[i].reg,
7239a0bf528SMauro Carvalho Chehab 			cx24123_regdata[i].data);
7249a0bf528SMauro Carvalho Chehab 
7259a0bf528SMauro Carvalho Chehab 	/* Set the LNB polarity */
7269a0bf528SMauro Carvalho Chehab 	if (state->config->lnb_polarity)
7279a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x32,
7289a0bf528SMauro Carvalho Chehab 			cx24123_readreg(state, 0x32) | 0x02);
7299a0bf528SMauro Carvalho Chehab 
7309a0bf528SMauro Carvalho Chehab 	if (state->config->dont_use_pll)
7319a0bf528SMauro Carvalho Chehab 		cx24123_repeater_mode(state, 1, 0);
7329a0bf528SMauro Carvalho Chehab 
7339a0bf528SMauro Carvalho Chehab 	return 0;
7349a0bf528SMauro Carvalho Chehab }
7359a0bf528SMauro Carvalho Chehab 
7369a0bf528SMauro Carvalho Chehab static int cx24123_set_voltage(struct dvb_frontend *fe,
7379a0bf528SMauro Carvalho Chehab 	fe_sec_voltage_t voltage)
7389a0bf528SMauro Carvalho Chehab {
7399a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
7409a0bf528SMauro Carvalho Chehab 	u8 val;
7419a0bf528SMauro Carvalho Chehab 
7429a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x29) & ~0x40;
7439a0bf528SMauro Carvalho Chehab 
7449a0bf528SMauro Carvalho Chehab 	switch (voltage) {
7459a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_13:
7469a0bf528SMauro Carvalho Chehab 		dprintk("setting voltage 13V\n");
7479a0bf528SMauro Carvalho Chehab 		return cx24123_writereg(state, 0x29, val & 0x7f);
7489a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_18:
7499a0bf528SMauro Carvalho Chehab 		dprintk("setting voltage 18V\n");
7509a0bf528SMauro Carvalho Chehab 		return cx24123_writereg(state, 0x29, val | 0x80);
7519a0bf528SMauro Carvalho Chehab 	case SEC_VOLTAGE_OFF:
7529a0bf528SMauro Carvalho Chehab 		/* already handled in cx88-dvb */
7539a0bf528SMauro Carvalho Chehab 		return 0;
7549a0bf528SMauro Carvalho Chehab 	default:
7559a0bf528SMauro Carvalho Chehab 		return -EINVAL;
7569a0bf528SMauro Carvalho Chehab 	};
7579a0bf528SMauro Carvalho Chehab 
7589a0bf528SMauro Carvalho Chehab 	return 0;
7599a0bf528SMauro Carvalho Chehab }
7609a0bf528SMauro Carvalho Chehab 
7619a0bf528SMauro Carvalho Chehab /* wait for diseqc queue to become ready (or timeout) */
7629a0bf528SMauro Carvalho Chehab static void cx24123_wait_for_diseqc(struct cx24123_state *state)
7639a0bf528SMauro Carvalho Chehab {
7649a0bf528SMauro Carvalho Chehab 	unsigned long timeout = jiffies + msecs_to_jiffies(200);
7659a0bf528SMauro Carvalho Chehab 	while (!(cx24123_readreg(state, 0x29) & 0x40)) {
7669a0bf528SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
7679a0bf528SMauro Carvalho Chehab 			err("%s: diseqc queue not ready, " \
7689a0bf528SMauro Carvalho Chehab 				"command may be lost.\n", __func__);
7699a0bf528SMauro Carvalho Chehab 			break;
7709a0bf528SMauro Carvalho Chehab 		}
7719a0bf528SMauro Carvalho Chehab 		msleep(10);
7729a0bf528SMauro Carvalho Chehab 	}
7739a0bf528SMauro Carvalho Chehab }
7749a0bf528SMauro Carvalho Chehab 
7759a0bf528SMauro Carvalho Chehab static int cx24123_send_diseqc_msg(struct dvb_frontend *fe,
7769a0bf528SMauro Carvalho Chehab 	struct dvb_diseqc_master_cmd *cmd)
7779a0bf528SMauro Carvalho Chehab {
7789a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
7799a0bf528SMauro Carvalho Chehab 	int i, val, tone;
7809a0bf528SMauro Carvalho Chehab 
7819a0bf528SMauro Carvalho Chehab 	dprintk("\n");
7829a0bf528SMauro Carvalho Chehab 
7839a0bf528SMauro Carvalho Chehab 	/* stop continuous tone if enabled */
7849a0bf528SMauro Carvalho Chehab 	tone = cx24123_readreg(state, 0x29);
7859a0bf528SMauro Carvalho Chehab 	if (tone & 0x10)
7869a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, tone & ~0x50);
7879a0bf528SMauro Carvalho Chehab 
7889a0bf528SMauro Carvalho Chehab 	/* wait for diseqc queue ready */
7899a0bf528SMauro Carvalho Chehab 	cx24123_wait_for_diseqc(state);
7909a0bf528SMauro Carvalho Chehab 
7919a0bf528SMauro Carvalho Chehab 	/* select tone mode */
7929a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb);
7939a0bf528SMauro Carvalho Chehab 
7949a0bf528SMauro Carvalho Chehab 	for (i = 0; i < cmd->msg_len; i++)
7959a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x2C + i, cmd->msg[i]);
7969a0bf528SMauro Carvalho Chehab 
7979a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x29);
7989a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) |
7999a0bf528SMauro Carvalho Chehab 		((cmd->msg_len-3) & 3));
8009a0bf528SMauro Carvalho Chehab 
8019a0bf528SMauro Carvalho Chehab 	/* wait for diseqc message to finish sending */
8029a0bf528SMauro Carvalho Chehab 	cx24123_wait_for_diseqc(state);
8039a0bf528SMauro Carvalho Chehab 
8049a0bf528SMauro Carvalho Chehab 	/* restart continuous tone if enabled */
8059a0bf528SMauro Carvalho Chehab 	if (tone & 0x10)
8069a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, tone & ~0x40);
8079a0bf528SMauro Carvalho Chehab 
8089a0bf528SMauro Carvalho Chehab 	return 0;
8099a0bf528SMauro Carvalho Chehab }
8109a0bf528SMauro Carvalho Chehab 
8119a0bf528SMauro Carvalho Chehab static int cx24123_diseqc_send_burst(struct dvb_frontend *fe,
8129a0bf528SMauro Carvalho Chehab 	fe_sec_mini_cmd_t burst)
8139a0bf528SMauro Carvalho Chehab {
8149a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
8159a0bf528SMauro Carvalho Chehab 	int val, tone;
8169a0bf528SMauro Carvalho Chehab 
8179a0bf528SMauro Carvalho Chehab 	dprintk("\n");
8189a0bf528SMauro Carvalho Chehab 
8199a0bf528SMauro Carvalho Chehab 	/* stop continuous tone if enabled */
8209a0bf528SMauro Carvalho Chehab 	tone = cx24123_readreg(state, 0x29);
8219a0bf528SMauro Carvalho Chehab 	if (tone & 0x10)
8229a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, tone & ~0x50);
8239a0bf528SMauro Carvalho Chehab 
8249a0bf528SMauro Carvalho Chehab 	/* wait for diseqc queue ready */
8259a0bf528SMauro Carvalho Chehab 	cx24123_wait_for_diseqc(state);
8269a0bf528SMauro Carvalho Chehab 
8279a0bf528SMauro Carvalho Chehab 	/* select tone mode */
8289a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4);
8299a0bf528SMauro Carvalho Chehab 	msleep(30);
8309a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x29);
8319a0bf528SMauro Carvalho Chehab 	if (burst == SEC_MINI_A)
8329a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00));
8339a0bf528SMauro Carvalho Chehab 	else if (burst == SEC_MINI_B)
8349a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08));
8359a0bf528SMauro Carvalho Chehab 	else
8369a0bf528SMauro Carvalho Chehab 		return -EINVAL;
8379a0bf528SMauro Carvalho Chehab 
8389a0bf528SMauro Carvalho Chehab 	cx24123_wait_for_diseqc(state);
8399a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb);
8409a0bf528SMauro Carvalho Chehab 
8419a0bf528SMauro Carvalho Chehab 	/* restart continuous tone if enabled */
8429a0bf528SMauro Carvalho Chehab 	if (tone & 0x10)
8439a0bf528SMauro Carvalho Chehab 		cx24123_writereg(state, 0x29, tone & ~0x40);
8449a0bf528SMauro Carvalho Chehab 
8459a0bf528SMauro Carvalho Chehab 	return 0;
8469a0bf528SMauro Carvalho Chehab }
8479a0bf528SMauro Carvalho Chehab 
8489a0bf528SMauro Carvalho Chehab static int cx24123_read_status(struct dvb_frontend *fe, fe_status_t *status)
8499a0bf528SMauro Carvalho Chehab {
8509a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
8519a0bf528SMauro Carvalho Chehab 	int sync = cx24123_readreg(state, 0x14);
8529a0bf528SMauro Carvalho Chehab 
8539a0bf528SMauro Carvalho Chehab 	*status = 0;
8549a0bf528SMauro Carvalho Chehab 	if (state->config->dont_use_pll) {
8559a0bf528SMauro Carvalho Chehab 		u32 tun_status = 0;
8569a0bf528SMauro Carvalho Chehab 		if (fe->ops.tuner_ops.get_status)
8579a0bf528SMauro Carvalho Chehab 			fe->ops.tuner_ops.get_status(fe, &tun_status);
8589a0bf528SMauro Carvalho Chehab 		if (tun_status & TUNER_STATUS_LOCKED)
8599a0bf528SMauro Carvalho Chehab 			*status |= FE_HAS_SIGNAL;
8609a0bf528SMauro Carvalho Chehab 	} else {
8619a0bf528SMauro Carvalho Chehab 		int lock = cx24123_readreg(state, 0x20);
8629a0bf528SMauro Carvalho Chehab 		if (lock & 0x01)
8639a0bf528SMauro Carvalho Chehab 			*status |= FE_HAS_SIGNAL;
8649a0bf528SMauro Carvalho Chehab 	}
8659a0bf528SMauro Carvalho Chehab 
8669a0bf528SMauro Carvalho Chehab 	if (sync & 0x02)
8679a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;	/* Phase locked */
8689a0bf528SMauro Carvalho Chehab 	if (sync & 0x04)
8699a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
8709a0bf528SMauro Carvalho Chehab 
8719a0bf528SMauro Carvalho Chehab 	/* Reed-Solomon Status */
8729a0bf528SMauro Carvalho Chehab 	if (sync & 0x08)
8739a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
8749a0bf528SMauro Carvalho Chehab 	if (sync & 0x80)
8759a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;		/*Full Sync */
8769a0bf528SMauro Carvalho Chehab 
8779a0bf528SMauro Carvalho Chehab 	return 0;
8789a0bf528SMauro Carvalho Chehab }
8799a0bf528SMauro Carvalho Chehab 
8809a0bf528SMauro Carvalho Chehab /*
8819a0bf528SMauro Carvalho Chehab  * Configured to return the measurement of errors in blocks,
8829a0bf528SMauro Carvalho Chehab  * because no UCBLOCKS value is available, so this value doubles up
8839a0bf528SMauro Carvalho Chehab  * to satisfy both measurements.
8849a0bf528SMauro Carvalho Chehab  */
8859a0bf528SMauro Carvalho Chehab static int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber)
8869a0bf528SMauro Carvalho Chehab {
8879a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
8889a0bf528SMauro Carvalho Chehab 
8899a0bf528SMauro Carvalho Chehab 	/* The true bit error rate is this value divided by
8909a0bf528SMauro Carvalho Chehab 	   the window size (set as 256 * 255) */
8919a0bf528SMauro Carvalho Chehab 	*ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
8929a0bf528SMauro Carvalho Chehab 		(cx24123_readreg(state, 0x1d) << 8 |
8939a0bf528SMauro Carvalho Chehab 		 cx24123_readreg(state, 0x1e));
8949a0bf528SMauro Carvalho Chehab 
8959a0bf528SMauro Carvalho Chehab 	dprintk("BER = %d\n", *ber);
8969a0bf528SMauro Carvalho Chehab 
8979a0bf528SMauro Carvalho Chehab 	return 0;
8989a0bf528SMauro Carvalho Chehab }
8999a0bf528SMauro Carvalho Chehab 
9009a0bf528SMauro Carvalho Chehab static int cx24123_read_signal_strength(struct dvb_frontend *fe,
9019a0bf528SMauro Carvalho Chehab 	u16 *signal_strength)
9029a0bf528SMauro Carvalho Chehab {
9039a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
9049a0bf528SMauro Carvalho Chehab 
9059a0bf528SMauro Carvalho Chehab 	/* larger = better */
9069a0bf528SMauro Carvalho Chehab 	*signal_strength = cx24123_readreg(state, 0x3b) << 8;
9079a0bf528SMauro Carvalho Chehab 
9089a0bf528SMauro Carvalho Chehab 	dprintk("Signal strength = %d\n", *signal_strength);
9099a0bf528SMauro Carvalho Chehab 
9109a0bf528SMauro Carvalho Chehab 	return 0;
9119a0bf528SMauro Carvalho Chehab }
9129a0bf528SMauro Carvalho Chehab 
9139a0bf528SMauro Carvalho Chehab static int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr)
9149a0bf528SMauro Carvalho Chehab {
9159a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
9169a0bf528SMauro Carvalho Chehab 
9179a0bf528SMauro Carvalho Chehab 	/* Inverted raw Es/N0 count, totally bogus but better than the
9189a0bf528SMauro Carvalho Chehab 	   BER threshold. */
9199a0bf528SMauro Carvalho Chehab 	*snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) |
9209a0bf528SMauro Carvalho Chehab 			 (u16)cx24123_readreg(state, 0x19));
9219a0bf528SMauro Carvalho Chehab 
9229a0bf528SMauro Carvalho Chehab 	dprintk("read S/N index = %d\n", *snr);
9239a0bf528SMauro Carvalho Chehab 
9249a0bf528SMauro Carvalho Chehab 	return 0;
9259a0bf528SMauro Carvalho Chehab }
9269a0bf528SMauro Carvalho Chehab 
9279a0bf528SMauro Carvalho Chehab static int cx24123_set_frontend(struct dvb_frontend *fe)
9289a0bf528SMauro Carvalho Chehab {
9299a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
9309a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
9319a0bf528SMauro Carvalho Chehab 
9329a0bf528SMauro Carvalho Chehab 	dprintk("\n");
9339a0bf528SMauro Carvalho Chehab 
9349a0bf528SMauro Carvalho Chehab 	if (state->config->set_ts_params)
9359a0bf528SMauro Carvalho Chehab 		state->config->set_ts_params(fe, 0);
9369a0bf528SMauro Carvalho Chehab 
9379a0bf528SMauro Carvalho Chehab 	state->currentfreq = p->frequency;
9389a0bf528SMauro Carvalho Chehab 	state->currentsymbolrate = p->symbol_rate;
9399a0bf528SMauro Carvalho Chehab 
9409a0bf528SMauro Carvalho Chehab 	cx24123_set_inversion(state, p->inversion);
9419a0bf528SMauro Carvalho Chehab 	cx24123_set_fec(state, p->fec_inner);
9429a0bf528SMauro Carvalho Chehab 	cx24123_set_symbolrate(state, p->symbol_rate);
9439a0bf528SMauro Carvalho Chehab 
9449a0bf528SMauro Carvalho Chehab 	if (!state->config->dont_use_pll)
9459a0bf528SMauro Carvalho Chehab 		cx24123_pll_tune(fe);
9469a0bf528SMauro Carvalho Chehab 	else if (fe->ops.tuner_ops.set_params)
9479a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
9489a0bf528SMauro Carvalho Chehab 	else
9499a0bf528SMauro Carvalho Chehab 		err("it seems I don't have a tuner...");
9509a0bf528SMauro Carvalho Chehab 
9519a0bf528SMauro Carvalho Chehab 	/* Enable automatic acquisition and reset cycle */
9529a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07));
9539a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x00, 0x10);
9549a0bf528SMauro Carvalho Chehab 	cx24123_writereg(state, 0x00, 0);
9559a0bf528SMauro Carvalho Chehab 
9569a0bf528SMauro Carvalho Chehab 	if (state->config->agc_callback)
9579a0bf528SMauro Carvalho Chehab 		state->config->agc_callback(fe);
9589a0bf528SMauro Carvalho Chehab 
9599a0bf528SMauro Carvalho Chehab 	return 0;
9609a0bf528SMauro Carvalho Chehab }
9619a0bf528SMauro Carvalho Chehab 
9629a0bf528SMauro Carvalho Chehab static int cx24123_get_frontend(struct dvb_frontend *fe)
9639a0bf528SMauro Carvalho Chehab {
9649a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
9659a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
9669a0bf528SMauro Carvalho Chehab 
9679a0bf528SMauro Carvalho Chehab 	dprintk("\n");
9689a0bf528SMauro Carvalho Chehab 
9699a0bf528SMauro Carvalho Chehab 	if (cx24123_get_inversion(state, &p->inversion) != 0) {
9709a0bf528SMauro Carvalho Chehab 		err("%s: Failed to get inversion status\n", __func__);
9719a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
9729a0bf528SMauro Carvalho Chehab 	}
9739a0bf528SMauro Carvalho Chehab 	if (cx24123_get_fec(state, &p->fec_inner) != 0) {
9749a0bf528SMauro Carvalho Chehab 		err("%s: Failed to get fec status\n", __func__);
9759a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
9769a0bf528SMauro Carvalho Chehab 	}
9779a0bf528SMauro Carvalho Chehab 	p->frequency = state->currentfreq;
9789a0bf528SMauro Carvalho Chehab 	p->symbol_rate = state->currentsymbolrate;
9799a0bf528SMauro Carvalho Chehab 
9809a0bf528SMauro Carvalho Chehab 	return 0;
9819a0bf528SMauro Carvalho Chehab }
9829a0bf528SMauro Carvalho Chehab 
9839a0bf528SMauro Carvalho Chehab static int cx24123_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
9849a0bf528SMauro Carvalho Chehab {
9859a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
9869a0bf528SMauro Carvalho Chehab 	u8 val;
9879a0bf528SMauro Carvalho Chehab 
9889a0bf528SMauro Carvalho Chehab 	/* wait for diseqc queue ready */
9899a0bf528SMauro Carvalho Chehab 	cx24123_wait_for_diseqc(state);
9909a0bf528SMauro Carvalho Chehab 
9919a0bf528SMauro Carvalho Chehab 	val = cx24123_readreg(state, 0x29) & ~0x40;
9929a0bf528SMauro Carvalho Chehab 
9939a0bf528SMauro Carvalho Chehab 	switch (tone) {
9949a0bf528SMauro Carvalho Chehab 	case SEC_TONE_ON:
9959a0bf528SMauro Carvalho Chehab 		dprintk("setting tone on\n");
9969a0bf528SMauro Carvalho Chehab 		return cx24123_writereg(state, 0x29, val | 0x10);
9979a0bf528SMauro Carvalho Chehab 	case SEC_TONE_OFF:
9989a0bf528SMauro Carvalho Chehab 		dprintk("setting tone off\n");
9999a0bf528SMauro Carvalho Chehab 		return cx24123_writereg(state, 0x29, val & 0xef);
10009a0bf528SMauro Carvalho Chehab 	default:
10019a0bf528SMauro Carvalho Chehab 		err("CASE reached default with tone=%d\n", tone);
10029a0bf528SMauro Carvalho Chehab 		return -EINVAL;
10039a0bf528SMauro Carvalho Chehab 	}
10049a0bf528SMauro Carvalho Chehab 
10059a0bf528SMauro Carvalho Chehab 	return 0;
10069a0bf528SMauro Carvalho Chehab }
10079a0bf528SMauro Carvalho Chehab 
10089a0bf528SMauro Carvalho Chehab static int cx24123_tune(struct dvb_frontend *fe,
10099a0bf528SMauro Carvalho Chehab 			bool re_tune,
10109a0bf528SMauro Carvalho Chehab 			unsigned int mode_flags,
10119a0bf528SMauro Carvalho Chehab 			unsigned int *delay,
10129a0bf528SMauro Carvalho Chehab 			fe_status_t *status)
10139a0bf528SMauro Carvalho Chehab {
10149a0bf528SMauro Carvalho Chehab 	int retval = 0;
10159a0bf528SMauro Carvalho Chehab 
10169a0bf528SMauro Carvalho Chehab 	if (re_tune)
10179a0bf528SMauro Carvalho Chehab 		retval = cx24123_set_frontend(fe);
10189a0bf528SMauro Carvalho Chehab 
10199a0bf528SMauro Carvalho Chehab 	if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
10209a0bf528SMauro Carvalho Chehab 		cx24123_read_status(fe, status);
10219a0bf528SMauro Carvalho Chehab 	*delay = HZ/10;
10229a0bf528SMauro Carvalho Chehab 
10239a0bf528SMauro Carvalho Chehab 	return retval;
10249a0bf528SMauro Carvalho Chehab }
10259a0bf528SMauro Carvalho Chehab 
10269a0bf528SMauro Carvalho Chehab static int cx24123_get_algo(struct dvb_frontend *fe)
10279a0bf528SMauro Carvalho Chehab {
10289a0bf528SMauro Carvalho Chehab 	return 1; /* FE_ALGO_HW */
10299a0bf528SMauro Carvalho Chehab }
10309a0bf528SMauro Carvalho Chehab 
10319a0bf528SMauro Carvalho Chehab static void cx24123_release(struct dvb_frontend *fe)
10329a0bf528SMauro Carvalho Chehab {
10339a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
10349a0bf528SMauro Carvalho Chehab 	dprintk("\n");
10359a0bf528SMauro Carvalho Chehab 	i2c_del_adapter(&state->tuner_i2c_adapter);
10369a0bf528SMauro Carvalho Chehab 	kfree(state);
10379a0bf528SMauro Carvalho Chehab }
10389a0bf528SMauro Carvalho Chehab 
10399a0bf528SMauro Carvalho Chehab static int cx24123_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap,
10409a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[], int num)
10419a0bf528SMauro Carvalho Chehab {
10429a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = i2c_get_adapdata(i2c_adap);
10439a0bf528SMauro Carvalho Chehab 	/* this repeater closes after the first stop */
10449a0bf528SMauro Carvalho Chehab 	cx24123_repeater_mode(state, 1, 1);
10459a0bf528SMauro Carvalho Chehab 	return i2c_transfer(state->i2c, msg, num);
10469a0bf528SMauro Carvalho Chehab }
10479a0bf528SMauro Carvalho Chehab 
10489a0bf528SMauro Carvalho Chehab static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter)
10499a0bf528SMauro Carvalho Chehab {
10509a0bf528SMauro Carvalho Chehab 	return I2C_FUNC_I2C;
10519a0bf528SMauro Carvalho Chehab }
10529a0bf528SMauro Carvalho Chehab 
10539a0bf528SMauro Carvalho Chehab static struct i2c_algorithm cx24123_tuner_i2c_algo = {
10549a0bf528SMauro Carvalho Chehab 	.master_xfer   = cx24123_tuner_i2c_tuner_xfer,
10559a0bf528SMauro Carvalho Chehab 	.functionality = cx24123_tuner_i2c_func,
10569a0bf528SMauro Carvalho Chehab };
10579a0bf528SMauro Carvalho Chehab 
10589a0bf528SMauro Carvalho Chehab struct i2c_adapter *
10599a0bf528SMauro Carvalho Chehab 	cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe)
10609a0bf528SMauro Carvalho Chehab {
10619a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state = fe->demodulator_priv;
10629a0bf528SMauro Carvalho Chehab 	return &state->tuner_i2c_adapter;
10639a0bf528SMauro Carvalho Chehab }
10649a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter);
10659a0bf528SMauro Carvalho Chehab 
10669a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24123_ops;
10679a0bf528SMauro Carvalho Chehab 
10689a0bf528SMauro Carvalho Chehab struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
10699a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
10709a0bf528SMauro Carvalho Chehab {
10719a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
10729a0bf528SMauro Carvalho Chehab 	struct cx24123_state *state =
10739a0bf528SMauro Carvalho Chehab 		kzalloc(sizeof(struct cx24123_state), GFP_KERNEL);
10749a0bf528SMauro Carvalho Chehab 
10759a0bf528SMauro Carvalho Chehab 	dprintk("\n");
10769a0bf528SMauro Carvalho Chehab 	if (state == NULL) {
10779a0bf528SMauro Carvalho Chehab 		err("Unable to kzalloc\n");
10789a0bf528SMauro Carvalho Chehab 		goto error;
10799a0bf528SMauro Carvalho Chehab 	}
10809a0bf528SMauro Carvalho Chehab 
10819a0bf528SMauro Carvalho Chehab 	/* setup the state */
10829a0bf528SMauro Carvalho Chehab 	state->config = config;
10839a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
10849a0bf528SMauro Carvalho Chehab 
10859a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
10869a0bf528SMauro Carvalho Chehab 	state->demod_rev = cx24123_readreg(state, 0x00);
10879a0bf528SMauro Carvalho Chehab 	switch (state->demod_rev) {
10889a0bf528SMauro Carvalho Chehab 	case 0xe1:
10899a0bf528SMauro Carvalho Chehab 		info("detected CX24123C\n");
10909a0bf528SMauro Carvalho Chehab 		break;
10919a0bf528SMauro Carvalho Chehab 	case 0xd1:
10929a0bf528SMauro Carvalho Chehab 		info("detected CX24123\n");
10939a0bf528SMauro Carvalho Chehab 		break;
10949a0bf528SMauro Carvalho Chehab 	default:
10959a0bf528SMauro Carvalho Chehab 		err("wrong demod revision: %x\n", state->demod_rev);
10969a0bf528SMauro Carvalho Chehab 		goto error;
10979a0bf528SMauro Carvalho Chehab 	}
10989a0bf528SMauro Carvalho Chehab 
10999a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
11009a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &cx24123_ops,
11019a0bf528SMauro Carvalho Chehab 		sizeof(struct dvb_frontend_ops));
11029a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
11039a0bf528SMauro Carvalho Chehab 
11049a0bf528SMauro Carvalho Chehab 	/* create tuner i2c adapter */
11059a0bf528SMauro Carvalho Chehab 	if (config->dont_use_pll)
11069a0bf528SMauro Carvalho Chehab 		cx24123_repeater_mode(state, 1, 0);
11079a0bf528SMauro Carvalho Chehab 
11089a0bf528SMauro Carvalho Chehab 	strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus",
11099a0bf528SMauro Carvalho Chehab 		sizeof(state->tuner_i2c_adapter.name));
11109a0bf528SMauro Carvalho Chehab 	state->tuner_i2c_adapter.algo      = &cx24123_tuner_i2c_algo;
11119a0bf528SMauro Carvalho Chehab 	state->tuner_i2c_adapter.algo_data = NULL;
11129a0bf528SMauro Carvalho Chehab 	i2c_set_adapdata(&state->tuner_i2c_adapter, state);
11139a0bf528SMauro Carvalho Chehab 	if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) {
11149a0bf528SMauro Carvalho Chehab 		err("tuner i2c bus could not be initialized\n");
11159a0bf528SMauro Carvalho Chehab 		goto error;
11169a0bf528SMauro Carvalho Chehab 	}
11179a0bf528SMauro Carvalho Chehab 
11189a0bf528SMauro Carvalho Chehab 	return &state->frontend;
11199a0bf528SMauro Carvalho Chehab 
11209a0bf528SMauro Carvalho Chehab error:
11219a0bf528SMauro Carvalho Chehab 	kfree(state);
11229a0bf528SMauro Carvalho Chehab 
11239a0bf528SMauro Carvalho Chehab 	return NULL;
11249a0bf528SMauro Carvalho Chehab }
11259a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(cx24123_attach);
11269a0bf528SMauro Carvalho Chehab 
11279a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24123_ops = {
11289a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBS },
11299a0bf528SMauro Carvalho Chehab 	.info = {
11309a0bf528SMauro Carvalho Chehab 		.name = "Conexant CX24123/CX24109",
11319a0bf528SMauro Carvalho Chehab 		.frequency_min = 950000,
11329a0bf528SMauro Carvalho Chehab 		.frequency_max = 2150000,
11339a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
11349a0bf528SMauro Carvalho Chehab 		.frequency_tolerance = 5000,
11359a0bf528SMauro Carvalho Chehab 		.symbol_rate_min = 1000000,
11369a0bf528SMauro Carvalho Chehab 		.symbol_rate_max = 45000000,
11379a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
11389a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
11399a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
11409a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
11419a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK | FE_CAN_RECOVER
11429a0bf528SMauro Carvalho Chehab 	},
11439a0bf528SMauro Carvalho Chehab 
11449a0bf528SMauro Carvalho Chehab 	.release = cx24123_release,
11459a0bf528SMauro Carvalho Chehab 
11469a0bf528SMauro Carvalho Chehab 	.init = cx24123_initfe,
11479a0bf528SMauro Carvalho Chehab 	.set_frontend = cx24123_set_frontend,
11489a0bf528SMauro Carvalho Chehab 	.get_frontend = cx24123_get_frontend,
11499a0bf528SMauro Carvalho Chehab 	.read_status = cx24123_read_status,
11509a0bf528SMauro Carvalho Chehab 	.read_ber = cx24123_read_ber,
11519a0bf528SMauro Carvalho Chehab 	.read_signal_strength = cx24123_read_signal_strength,
11529a0bf528SMauro Carvalho Chehab 	.read_snr = cx24123_read_snr,
11539a0bf528SMauro Carvalho Chehab 	.diseqc_send_master_cmd = cx24123_send_diseqc_msg,
11549a0bf528SMauro Carvalho Chehab 	.diseqc_send_burst = cx24123_diseqc_send_burst,
11559a0bf528SMauro Carvalho Chehab 	.set_tone = cx24123_set_tone,
11569a0bf528SMauro Carvalho Chehab 	.set_voltage = cx24123_set_voltage,
11579a0bf528SMauro Carvalho Chehab 	.tune = cx24123_tune,
11589a0bf528SMauro Carvalho Chehab 	.get_frontend_algo = cx24123_get_algo,
11599a0bf528SMauro Carvalho Chehab };
11609a0bf528SMauro Carvalho Chehab 
11619a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB Frontend module for Conexant " \
11629a0bf528SMauro Carvalho Chehab 	"CX24123/CX24109/CX24113 hardware");
11639a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth");
11649a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
11659a0bf528SMauro Carvalho Chehab 
1166