xref: /linux/drivers/media/dvb-frontends/stv0297.c (revision 4bd69e7b9c1b8c1a5b6cfc50a126ae0a1d926e57)
19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     Driver for STV0297 demodulator
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
59a0bf528SMauro Carvalho Chehab     Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
69a0bf528SMauro Carvalho Chehab 
79a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
89a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
99a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
109a0bf528SMauro Carvalho Chehab     (at your option) any later version.
119a0bf528SMauro Carvalho Chehab 
129a0bf528SMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
139a0bf528SMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
149a0bf528SMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
159a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
189a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
199a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
209a0bf528SMauro Carvalho Chehab */
219a0bf528SMauro Carvalho Chehab 
229a0bf528SMauro Carvalho Chehab #include <linux/init.h>
239a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
249a0bf528SMauro Carvalho Chehab #include <linux/module.h>
259a0bf528SMauro Carvalho Chehab #include <linux/string.h>
269a0bf528SMauro Carvalho Chehab #include <linux/delay.h>
279a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h>
289a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
299a0bf528SMauro Carvalho Chehab 
309a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
319a0bf528SMauro Carvalho Chehab #include "stv0297.h"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab struct stv0297_state {
349a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
359a0bf528SMauro Carvalho Chehab 	const struct stv0297_config *config;
369a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
379a0bf528SMauro Carvalho Chehab 
389a0bf528SMauro Carvalho Chehab 	unsigned long last_ber;
399a0bf528SMauro Carvalho Chehab 	unsigned long base_freq;
409a0bf528SMauro Carvalho Chehab };
419a0bf528SMauro Carvalho Chehab 
429a0bf528SMauro Carvalho Chehab #if 1
439a0bf528SMauro Carvalho Chehab #define dprintk(x...) printk(x)
449a0bf528SMauro Carvalho Chehab #else
459a0bf528SMauro Carvalho Chehab #define dprintk(x...)
469a0bf528SMauro Carvalho Chehab #endif
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab #define STV0297_CLOCK_KHZ   28900
499a0bf528SMauro Carvalho Chehab 
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data)
529a0bf528SMauro Carvalho Chehab {
539a0bf528SMauro Carvalho Chehab 	int ret;
549a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
559a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 };
569a0bf528SMauro Carvalho Chehab 
579a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab 	if (ret != 1)
60*4bd69e7bSMauro Carvalho Chehab 		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
61*4bd69e7bSMauro Carvalho Chehab 			__func__, reg, data, ret);
629a0bf528SMauro Carvalho Chehab 
639a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -1 : 0;
649a0bf528SMauro Carvalho Chehab }
659a0bf528SMauro Carvalho Chehab 
669a0bf528SMauro Carvalho Chehab static int stv0297_readreg(struct stv0297_state *state, u8 reg)
679a0bf528SMauro Carvalho Chehab {
689a0bf528SMauro Carvalho Chehab 	int ret;
699a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
709a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
719a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1},
729a0bf528SMauro Carvalho Chehab 				 {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
739a0bf528SMauro Carvalho Chehab 			       };
749a0bf528SMauro Carvalho Chehab 
759a0bf528SMauro Carvalho Chehab 	// this device needs a STOP between the register and data
769a0bf528SMauro Carvalho Chehab 	if (state->config->stop_during_read) {
779a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
789a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret);
799a0bf528SMauro Carvalho Chehab 			return -1;
809a0bf528SMauro Carvalho Chehab 		}
819a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
829a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret);
839a0bf528SMauro Carvalho Chehab 			return -1;
849a0bf528SMauro Carvalho Chehab 		}
859a0bf528SMauro Carvalho Chehab 	} else {
869a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) {
879a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret);
889a0bf528SMauro Carvalho Chehab 			return -1;
899a0bf528SMauro Carvalho Chehab 		}
909a0bf528SMauro Carvalho Chehab 	}
919a0bf528SMauro Carvalho Chehab 
929a0bf528SMauro Carvalho Chehab 	return b1[0];
939a0bf528SMauro Carvalho Chehab }
949a0bf528SMauro Carvalho Chehab 
959a0bf528SMauro Carvalho Chehab static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data)
969a0bf528SMauro Carvalho Chehab {
979a0bf528SMauro Carvalho Chehab 	int val;
989a0bf528SMauro Carvalho Chehab 
999a0bf528SMauro Carvalho Chehab 	val = stv0297_readreg(state, reg);
1009a0bf528SMauro Carvalho Chehab 	val &= ~mask;
1019a0bf528SMauro Carvalho Chehab 	val |= (data & mask);
1029a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, reg, val);
1039a0bf528SMauro Carvalho Chehab 
1049a0bf528SMauro Carvalho Chehab 	return 0;
1059a0bf528SMauro Carvalho Chehab }
1069a0bf528SMauro Carvalho Chehab 
1079a0bf528SMauro Carvalho Chehab static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len)
1089a0bf528SMauro Carvalho Chehab {
1099a0bf528SMauro Carvalho Chehab 	int ret;
1109a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf =
1119a0bf528SMauro Carvalho Chehab 				  &reg1,.len = 1},
1129a0bf528SMauro Carvalho Chehab 	{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len}
1139a0bf528SMauro Carvalho Chehab 	};
1149a0bf528SMauro Carvalho Chehab 
1159a0bf528SMauro Carvalho Chehab 	// this device needs a STOP between the register and data
1169a0bf528SMauro Carvalho Chehab 	if (state->config->stop_during_read) {
1179a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
1189a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret);
1199a0bf528SMauro Carvalho Chehab 			return -1;
1209a0bf528SMauro Carvalho Chehab 		}
1219a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
1229a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret);
1239a0bf528SMauro Carvalho Chehab 			return -1;
1249a0bf528SMauro Carvalho Chehab 		}
1259a0bf528SMauro Carvalho Chehab 	} else {
1269a0bf528SMauro Carvalho Chehab 		if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) {
1279a0bf528SMauro Carvalho Chehab 			dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret);
1289a0bf528SMauro Carvalho Chehab 			return -1;
1299a0bf528SMauro Carvalho Chehab 		}
1309a0bf528SMauro Carvalho Chehab 	}
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 	return 0;
1339a0bf528SMauro Carvalho Chehab }
1349a0bf528SMauro Carvalho Chehab 
1359a0bf528SMauro Carvalho Chehab static u32 stv0297_get_symbolrate(struct stv0297_state *state)
1369a0bf528SMauro Carvalho Chehab {
1379a0bf528SMauro Carvalho Chehab 	u64 tmp;
1389a0bf528SMauro Carvalho Chehab 
1395e6b681bSMauro Carvalho Chehab 	tmp = (u64)(stv0297_readreg(state, 0x55)
1405e6b681bSMauro Carvalho Chehab 		    | (stv0297_readreg(state, 0x56) << 8)
1415e6b681bSMauro Carvalho Chehab 		    | (stv0297_readreg(state, 0x57) << 16)
1425e6b681bSMauro Carvalho Chehab 		    | (stv0297_readreg(state, 0x58) << 24));
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab 	tmp *= STV0297_CLOCK_KHZ;
1459a0bf528SMauro Carvalho Chehab 	tmp >>= 32;
1469a0bf528SMauro Carvalho Chehab 
1479a0bf528SMauro Carvalho Chehab 	return (u32) tmp;
1489a0bf528SMauro Carvalho Chehab }
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate)
1519a0bf528SMauro Carvalho Chehab {
1529a0bf528SMauro Carvalho Chehab 	long tmp;
1539a0bf528SMauro Carvalho Chehab 
1549a0bf528SMauro Carvalho Chehab 	tmp = 131072L * srate;	/* 131072 = 2^17  */
1559a0bf528SMauro Carvalho Chehab 	tmp = tmp / (STV0297_CLOCK_KHZ / 4);	/* 1/4 = 2^-2 */
1569a0bf528SMauro Carvalho Chehab 	tmp = tmp * 8192L;	/* 8192 = 2^13 */
1579a0bf528SMauro Carvalho Chehab 
1589a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF));
1599a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8));
1609a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16));
1619a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24));
1629a0bf528SMauro Carvalho Chehab }
1639a0bf528SMauro Carvalho Chehab 
1649a0bf528SMauro Carvalho Chehab static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate)
1659a0bf528SMauro Carvalho Chehab {
1669a0bf528SMauro Carvalho Chehab 	long tmp;
1679a0bf528SMauro Carvalho Chehab 
1689a0bf528SMauro Carvalho Chehab 	tmp = (long) fshift *262144L;	/* 262144 = 2*18 */
1699a0bf528SMauro Carvalho Chehab 	tmp /= symrate;
1709a0bf528SMauro Carvalho Chehab 	tmp *= 1024;		/* 1024 = 2*10   */
1719a0bf528SMauro Carvalho Chehab 
1729a0bf528SMauro Carvalho Chehab 	// adjust
1739a0bf528SMauro Carvalho Chehab 	if (tmp >= 0) {
1749a0bf528SMauro Carvalho Chehab 		tmp += 500000;
1759a0bf528SMauro Carvalho Chehab 	} else {
1769a0bf528SMauro Carvalho Chehab 		tmp -= 500000;
1779a0bf528SMauro Carvalho Chehab 	}
1789a0bf528SMauro Carvalho Chehab 	tmp /= 1000000;
1799a0bf528SMauro Carvalho Chehab 
1809a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x60, tmp & 0xFF);
1819a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0);
1829a0bf528SMauro Carvalho Chehab }
1839a0bf528SMauro Carvalho Chehab 
1849a0bf528SMauro Carvalho Chehab static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset)
1859a0bf528SMauro Carvalho Chehab {
1869a0bf528SMauro Carvalho Chehab 	long tmp;
1879a0bf528SMauro Carvalho Chehab 
1889a0bf528SMauro Carvalho Chehab 	/* symrate is hardcoded to 10000 */
1899a0bf528SMauro Carvalho Chehab 	tmp = offset * 26844L;	/* (2**28)/10000 */
1909a0bf528SMauro Carvalho Chehab 	if (tmp < 0)
1919a0bf528SMauro Carvalho Chehab 		tmp += 0x10000000;
1929a0bf528SMauro Carvalho Chehab 	tmp &= 0x0FFFFFFF;
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF));
1959a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8));
1969a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16));
1979a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f);
1989a0bf528SMauro Carvalho Chehab }
1999a0bf528SMauro Carvalho Chehab 
2009a0bf528SMauro Carvalho Chehab /*
2019a0bf528SMauro Carvalho Chehab static long stv0297_get_carrieroffset(struct stv0297_state *state)
2029a0bf528SMauro Carvalho Chehab {
2039a0bf528SMauro Carvalho Chehab 	s64 tmp;
2049a0bf528SMauro Carvalho Chehab 
2059a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x6B, 0x00);
2069a0bf528SMauro Carvalho Chehab 
2079a0bf528SMauro Carvalho Chehab 	tmp = stv0297_readreg(state, 0x66);
2089a0bf528SMauro Carvalho Chehab 	tmp |= (stv0297_readreg(state, 0x67) << 8);
2099a0bf528SMauro Carvalho Chehab 	tmp |= (stv0297_readreg(state, 0x68) << 16);
2109a0bf528SMauro Carvalho Chehab 	tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24;
2119a0bf528SMauro Carvalho Chehab 
2129a0bf528SMauro Carvalho Chehab 	tmp *= stv0297_get_symbolrate(state);
2139a0bf528SMauro Carvalho Chehab 	tmp >>= 28;
2149a0bf528SMauro Carvalho Chehab 
2159a0bf528SMauro Carvalho Chehab 	return (s32) tmp;
2169a0bf528SMauro Carvalho Chehab }
2179a0bf528SMauro Carvalho Chehab */
2189a0bf528SMauro Carvalho Chehab 
2199a0bf528SMauro Carvalho Chehab static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq)
2209a0bf528SMauro Carvalho Chehab {
2219a0bf528SMauro Carvalho Chehab 	s32 tmp;
2229a0bf528SMauro Carvalho Chehab 
2239a0bf528SMauro Carvalho Chehab 	if (freq > 10000)
2249a0bf528SMauro Carvalho Chehab 		freq -= STV0297_CLOCK_KHZ;
2259a0bf528SMauro Carvalho Chehab 
2269a0bf528SMauro Carvalho Chehab 	tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16);
2279a0bf528SMauro Carvalho Chehab 	tmp = (freq * 1000) / tmp;
2289a0bf528SMauro Carvalho Chehab 	if (tmp > 0xffff)
2299a0bf528SMauro Carvalho Chehab 		tmp = 0xffff;
2309a0bf528SMauro Carvalho Chehab 
2319a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x25, 0x80, 0x80);
2329a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x21, tmp >> 8);
2339a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x20, tmp);
2349a0bf528SMauro Carvalho Chehab }
2359a0bf528SMauro Carvalho Chehab 
2360df289a2SMauro Carvalho Chehab static int stv0297_set_qam(struct stv0297_state *state,
2370df289a2SMauro Carvalho Chehab 			   enum fe_modulation modulation)
2389a0bf528SMauro Carvalho Chehab {
2399a0bf528SMauro Carvalho Chehab 	int val = 0;
2409a0bf528SMauro Carvalho Chehab 
2419a0bf528SMauro Carvalho Chehab 	switch (modulation) {
2429a0bf528SMauro Carvalho Chehab 	case QAM_16:
2439a0bf528SMauro Carvalho Chehab 		val = 0;
2449a0bf528SMauro Carvalho Chehab 		break;
2459a0bf528SMauro Carvalho Chehab 
2469a0bf528SMauro Carvalho Chehab 	case QAM_32:
2479a0bf528SMauro Carvalho Chehab 		val = 1;
2489a0bf528SMauro Carvalho Chehab 		break;
2499a0bf528SMauro Carvalho Chehab 
2509a0bf528SMauro Carvalho Chehab 	case QAM_64:
2519a0bf528SMauro Carvalho Chehab 		val = 4;
2529a0bf528SMauro Carvalho Chehab 		break;
2539a0bf528SMauro Carvalho Chehab 
2549a0bf528SMauro Carvalho Chehab 	case QAM_128:
2559a0bf528SMauro Carvalho Chehab 		val = 2;
2569a0bf528SMauro Carvalho Chehab 		break;
2579a0bf528SMauro Carvalho Chehab 
2589a0bf528SMauro Carvalho Chehab 	case QAM_256:
2599a0bf528SMauro Carvalho Chehab 		val = 3;
2609a0bf528SMauro Carvalho Chehab 		break;
2619a0bf528SMauro Carvalho Chehab 
2629a0bf528SMauro Carvalho Chehab 	default:
2639a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2649a0bf528SMauro Carvalho Chehab 	}
2659a0bf528SMauro Carvalho Chehab 
2669a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x00, 0x70, val << 4);
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab 	return 0;
2699a0bf528SMauro Carvalho Chehab }
2709a0bf528SMauro Carvalho Chehab 
2710df289a2SMauro Carvalho Chehab static int stv0297_set_inversion(struct stv0297_state *state,
2720df289a2SMauro Carvalho Chehab 				 enum fe_spectral_inversion inversion)
2739a0bf528SMauro Carvalho Chehab {
2749a0bf528SMauro Carvalho Chehab 	int val = 0;
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab 	switch (inversion) {
2779a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
2789a0bf528SMauro Carvalho Chehab 		val = 0;
2799a0bf528SMauro Carvalho Chehab 		break;
2809a0bf528SMauro Carvalho Chehab 
2819a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
2829a0bf528SMauro Carvalho Chehab 		val = 1;
2839a0bf528SMauro Carvalho Chehab 		break;
2849a0bf528SMauro Carvalho Chehab 
2859a0bf528SMauro Carvalho Chehab 	default:
2869a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2879a0bf528SMauro Carvalho Chehab 	}
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x83, 0x08, val << 3);
2909a0bf528SMauro Carvalho Chehab 
2919a0bf528SMauro Carvalho Chehab 	return 0;
2929a0bf528SMauro Carvalho Chehab }
2939a0bf528SMauro Carvalho Chehab 
2949a0bf528SMauro Carvalho Chehab static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
2959a0bf528SMauro Carvalho Chehab {
2969a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
2979a0bf528SMauro Carvalho Chehab 
2989a0bf528SMauro Carvalho Chehab 	if (enable) {
2999a0bf528SMauro Carvalho Chehab 		stv0297_writereg(state, 0x87, 0x78);
3009a0bf528SMauro Carvalho Chehab 		stv0297_writereg(state, 0x86, 0xc8);
3019a0bf528SMauro Carvalho Chehab 	}
3029a0bf528SMauro Carvalho Chehab 
3039a0bf528SMauro Carvalho Chehab 	return 0;
3049a0bf528SMauro Carvalho Chehab }
3059a0bf528SMauro Carvalho Chehab 
3069a0bf528SMauro Carvalho Chehab static int stv0297_init(struct dvb_frontend *fe)
3079a0bf528SMauro Carvalho Chehab {
3089a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3099a0bf528SMauro Carvalho Chehab 	int i;
3109a0bf528SMauro Carvalho Chehab 
3119a0bf528SMauro Carvalho Chehab 	/* load init table */
3129a0bf528SMauro Carvalho Chehab 	for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2)
3139a0bf528SMauro Carvalho Chehab 		stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]);
3149a0bf528SMauro Carvalho Chehab 	msleep(200);
3159a0bf528SMauro Carvalho Chehab 
3169a0bf528SMauro Carvalho Chehab 	state->last_ber = 0;
3179a0bf528SMauro Carvalho Chehab 
3189a0bf528SMauro Carvalho Chehab 	return 0;
3199a0bf528SMauro Carvalho Chehab }
3209a0bf528SMauro Carvalho Chehab 
3219a0bf528SMauro Carvalho Chehab static int stv0297_sleep(struct dvb_frontend *fe)
3229a0bf528SMauro Carvalho Chehab {
3239a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3249a0bf528SMauro Carvalho Chehab 
3259a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x80, 1, 1);
3269a0bf528SMauro Carvalho Chehab 
3279a0bf528SMauro Carvalho Chehab 	return 0;
3289a0bf528SMauro Carvalho Chehab }
3299a0bf528SMauro Carvalho Chehab 
3300df289a2SMauro Carvalho Chehab static int stv0297_read_status(struct dvb_frontend *fe,
3310df289a2SMauro Carvalho Chehab 			       enum fe_status *status)
3329a0bf528SMauro Carvalho Chehab {
3339a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3349a0bf528SMauro Carvalho Chehab 
3359a0bf528SMauro Carvalho Chehab 	u8 sync = stv0297_readreg(state, 0xDF);
3369a0bf528SMauro Carvalho Chehab 
3379a0bf528SMauro Carvalho Chehab 	*status = 0;
3389a0bf528SMauro Carvalho Chehab 	if (sync & 0x80)
3399a0bf528SMauro Carvalho Chehab 		*status |=
3409a0bf528SMauro Carvalho Chehab 			FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
3419a0bf528SMauro Carvalho Chehab 	return 0;
3429a0bf528SMauro Carvalho Chehab }
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber)
3459a0bf528SMauro Carvalho Chehab {
3469a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3479a0bf528SMauro Carvalho Chehab 	u8 BER[3];
3489a0bf528SMauro Carvalho Chehab 
3499a0bf528SMauro Carvalho Chehab 	stv0297_readregs(state, 0xA0, BER, 3);
3509a0bf528SMauro Carvalho Chehab 	if (!(BER[0] & 0x80)) {
3519a0bf528SMauro Carvalho Chehab 		state->last_ber = BER[2] << 8 | BER[1];
3529a0bf528SMauro Carvalho Chehab 		stv0297_writereg_mask(state, 0xA0, 0x80, 0x80);
3539a0bf528SMauro Carvalho Chehab 	}
3549a0bf528SMauro Carvalho Chehab 
3559a0bf528SMauro Carvalho Chehab 	*ber = state->last_ber;
3569a0bf528SMauro Carvalho Chehab 
3579a0bf528SMauro Carvalho Chehab 	return 0;
3589a0bf528SMauro Carvalho Chehab }
3599a0bf528SMauro Carvalho Chehab 
3609a0bf528SMauro Carvalho Chehab 
3619a0bf528SMauro Carvalho Chehab static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
3629a0bf528SMauro Carvalho Chehab {
3639a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3649a0bf528SMauro Carvalho Chehab 	u8 STRENGTH[3];
3659a0bf528SMauro Carvalho Chehab 	u16 tmp;
3669a0bf528SMauro Carvalho Chehab 
3679a0bf528SMauro Carvalho Chehab 	stv0297_readregs(state, 0x41, STRENGTH, 3);
3689a0bf528SMauro Carvalho Chehab 	tmp = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0];
3699a0bf528SMauro Carvalho Chehab 	if (STRENGTH[2] & 0x20) {
3709a0bf528SMauro Carvalho Chehab 		if (tmp < 0x200)
3719a0bf528SMauro Carvalho Chehab 			tmp = 0;
3729a0bf528SMauro Carvalho Chehab 		else
3739a0bf528SMauro Carvalho Chehab 			tmp = tmp - 0x200;
3749a0bf528SMauro Carvalho Chehab 	} else {
3759a0bf528SMauro Carvalho Chehab 		if (tmp > 0x1ff)
3769a0bf528SMauro Carvalho Chehab 			tmp = 0;
3779a0bf528SMauro Carvalho Chehab 		else
3789a0bf528SMauro Carvalho Chehab 			tmp = 0x1ff - tmp;
3799a0bf528SMauro Carvalho Chehab 	}
3809a0bf528SMauro Carvalho Chehab 	*strength = (tmp << 7) | (tmp >> 2);
3819a0bf528SMauro Carvalho Chehab 	return 0;
3829a0bf528SMauro Carvalho Chehab }
3839a0bf528SMauro Carvalho Chehab 
3849a0bf528SMauro Carvalho Chehab static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr)
3859a0bf528SMauro Carvalho Chehab {
3869a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3879a0bf528SMauro Carvalho Chehab 	u8 SNR[2];
3889a0bf528SMauro Carvalho Chehab 
3899a0bf528SMauro Carvalho Chehab 	stv0297_readregs(state, 0x07, SNR, 2);
3909a0bf528SMauro Carvalho Chehab 	*snr = SNR[1] << 8 | SNR[0];
3919a0bf528SMauro Carvalho Chehab 
3929a0bf528SMauro Carvalho Chehab 	return 0;
3939a0bf528SMauro Carvalho Chehab }
3949a0bf528SMauro Carvalho Chehab 
3959a0bf528SMauro Carvalho Chehab static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
3969a0bf528SMauro Carvalho Chehab {
3979a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
3989a0bf528SMauro Carvalho Chehab 
3999a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */
4009a0bf528SMauro Carvalho Chehab 
4019a0bf528SMauro Carvalho Chehab 	*ucblocks = (stv0297_readreg(state, 0xD5) << 8)
4029a0bf528SMauro Carvalho Chehab 		| stv0297_readreg(state, 0xD4);
4039a0bf528SMauro Carvalho Chehab 
4049a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */
4059a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */
4069a0bf528SMauro Carvalho Chehab 
4079a0bf528SMauro Carvalho Chehab 	return 0;
4089a0bf528SMauro Carvalho Chehab }
4099a0bf528SMauro Carvalho Chehab 
4109a0bf528SMauro Carvalho Chehab static int stv0297_set_frontend(struct dvb_frontend *fe)
4119a0bf528SMauro Carvalho Chehab {
4129a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
4139a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
4149a0bf528SMauro Carvalho Chehab 	int u_threshold;
4159a0bf528SMauro Carvalho Chehab 	int initial_u;
4169a0bf528SMauro Carvalho Chehab 	int blind_u;
4179a0bf528SMauro Carvalho Chehab 	int delay;
4189a0bf528SMauro Carvalho Chehab 	int sweeprate;
4199a0bf528SMauro Carvalho Chehab 	int carrieroffset;
4209a0bf528SMauro Carvalho Chehab 	unsigned long timeout;
4210df289a2SMauro Carvalho Chehab 	enum fe_spectral_inversion inversion;
4229a0bf528SMauro Carvalho Chehab 
4239a0bf528SMauro Carvalho Chehab 	switch (p->modulation) {
4249a0bf528SMauro Carvalho Chehab 	case QAM_16:
4259a0bf528SMauro Carvalho Chehab 	case QAM_32:
4269a0bf528SMauro Carvalho Chehab 	case QAM_64:
4279a0bf528SMauro Carvalho Chehab 		delay = 100;
4289a0bf528SMauro Carvalho Chehab 		sweeprate = 1000;
4299a0bf528SMauro Carvalho Chehab 		break;
4309a0bf528SMauro Carvalho Chehab 
4319a0bf528SMauro Carvalho Chehab 	case QAM_128:
4329a0bf528SMauro Carvalho Chehab 	case QAM_256:
4339a0bf528SMauro Carvalho Chehab 		delay = 200;
4349a0bf528SMauro Carvalho Chehab 		sweeprate = 500;
4359a0bf528SMauro Carvalho Chehab 		break;
4369a0bf528SMauro Carvalho Chehab 
4379a0bf528SMauro Carvalho Chehab 	default:
4389a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4399a0bf528SMauro Carvalho Chehab 	}
4409a0bf528SMauro Carvalho Chehab 
4419a0bf528SMauro Carvalho Chehab 	// determine inversion dependent parameters
4429a0bf528SMauro Carvalho Chehab 	inversion = p->inversion;
4439a0bf528SMauro Carvalho Chehab 	if (state->config->invert)
4449a0bf528SMauro Carvalho Chehab 		inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
4459a0bf528SMauro Carvalho Chehab 	carrieroffset = -330;
4469a0bf528SMauro Carvalho Chehab 	switch (inversion) {
4479a0bf528SMauro Carvalho Chehab 	case INVERSION_OFF:
4489a0bf528SMauro Carvalho Chehab 		break;
4499a0bf528SMauro Carvalho Chehab 
4509a0bf528SMauro Carvalho Chehab 	case INVERSION_ON:
4519a0bf528SMauro Carvalho Chehab 		sweeprate = -sweeprate;
4529a0bf528SMauro Carvalho Chehab 		carrieroffset = -carrieroffset;
4539a0bf528SMauro Carvalho Chehab 		break;
4549a0bf528SMauro Carvalho Chehab 
4559a0bf528SMauro Carvalho Chehab 	default:
4569a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4579a0bf528SMauro Carvalho Chehab 	}
4589a0bf528SMauro Carvalho Chehab 
4599a0bf528SMauro Carvalho Chehab 	stv0297_init(fe);
4609a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
4619a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
4629a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
4639a0bf528SMauro Carvalho Chehab 	}
4649a0bf528SMauro Carvalho Chehab 
4659a0bf528SMauro Carvalho Chehab 	/* clear software interrupts */
4669a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x82, 0x0);
4679a0bf528SMauro Carvalho Chehab 
4689a0bf528SMauro Carvalho Chehab 	/* set initial demodulation frequency */
4699a0bf528SMauro Carvalho Chehab 	stv0297_set_initialdemodfreq(state, 7250);
4709a0bf528SMauro Carvalho Chehab 
4719a0bf528SMauro Carvalho Chehab 	/* setup AGC */
4729a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x43, 0x10, 0x00);
4739a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x41, 0x00);
4749a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x42, 0x03, 0x01);
4759a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x36, 0x60, 0x00);
4769a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x36, 0x18, 0x00);
4779a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x71, 0x80, 0x80);
4789a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x72, 0x00);
4799a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x73, 0x00);
4809a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x74, 0x0F, 0x00);
4819a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x43, 0x08, 0x00);
4829a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x71, 0x80, 0x00);
4839a0bf528SMauro Carvalho Chehab 
4849a0bf528SMauro Carvalho Chehab 	/* setup STL */
4859a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5a, 0x20, 0x20);
4869a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5b, 0x02, 0x02);
4879a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5b, 0x02, 0x00);
4889a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5b, 0x01, 0x00);
4899a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5a, 0x40, 0x40);
4909a0bf528SMauro Carvalho Chehab 
4919a0bf528SMauro Carvalho Chehab 	/* disable frequency sweep */
4929a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
4939a0bf528SMauro Carvalho Chehab 
4949a0bf528SMauro Carvalho Chehab 	/* reset deinterleaver */
4959a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x81, 0x01, 0x01);
4969a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x81, 0x01, 0x00);
4979a0bf528SMauro Carvalho Chehab 
4989a0bf528SMauro Carvalho Chehab 	/* ??? */
4999a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x83, 0x20, 0x20);
5009a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x83, 0x20, 0x00);
5019a0bf528SMauro Carvalho Chehab 
5029a0bf528SMauro Carvalho Chehab 	/* reset equaliser */
5039a0bf528SMauro Carvalho Chehab 	u_threshold = stv0297_readreg(state, 0x00) & 0xf;
5049a0bf528SMauro Carvalho Chehab 	initial_u = stv0297_readreg(state, 0x01) >> 4;
5059a0bf528SMauro Carvalho Chehab 	blind_u = stv0297_readreg(state, 0x01) & 0xf;
5069a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x84, 0x01, 0x01);
5079a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x84, 0x01, 0x00);
5089a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold);
5099a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4);
5109a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x01, 0x0f, blind_u);
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab 	/* data comes from internal A/D */
5139a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x87, 0x80, 0x00);
5149a0bf528SMauro Carvalho Chehab 
5159a0bf528SMauro Carvalho Chehab 	/* clear phase registers */
5169a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x63, 0x00);
5179a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x64, 0x00);
5189a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x65, 0x00);
5199a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x66, 0x00);
5209a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x67, 0x00);
5219a0bf528SMauro Carvalho Chehab 	stv0297_writereg(state, 0x68, 0x00);
5229a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x69, 0x0f, 0x00);
5239a0bf528SMauro Carvalho Chehab 
5249a0bf528SMauro Carvalho Chehab 	/* set parameters */
5259a0bf528SMauro Carvalho Chehab 	stv0297_set_qam(state, p->modulation);
5269a0bf528SMauro Carvalho Chehab 	stv0297_set_symbolrate(state, p->symbol_rate / 1000);
5279a0bf528SMauro Carvalho Chehab 	stv0297_set_sweeprate(state, sweeprate, p->symbol_rate / 1000);
5289a0bf528SMauro Carvalho Chehab 	stv0297_set_carrieroffset(state, carrieroffset);
5299a0bf528SMauro Carvalho Chehab 	stv0297_set_inversion(state, inversion);
5309a0bf528SMauro Carvalho Chehab 
5319a0bf528SMauro Carvalho Chehab 	/* kick off lock */
5329a0bf528SMauro Carvalho Chehab 	/* Disable corner detection for higher QAMs */
5339a0bf528SMauro Carvalho Chehab 	if (p->modulation == QAM_128 ||
5349a0bf528SMauro Carvalho Chehab 		p->modulation == QAM_256)
5359a0bf528SMauro Carvalho Chehab 		stv0297_writereg_mask(state, 0x88, 0x08, 0x00);
5369a0bf528SMauro Carvalho Chehab 	else
5379a0bf528SMauro Carvalho Chehab 		stv0297_writereg_mask(state, 0x88, 0x08, 0x08);
5389a0bf528SMauro Carvalho Chehab 
5399a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5a, 0x20, 0x00);
5409a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x6a, 0x01, 0x01);
5419a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x43, 0x40, 0x40);
5429a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5b, 0x30, 0x00);
5439a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c);
5449a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x03, 0x03, 0x03);
5459a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
5469a0bf528SMauro Carvalho Chehab 
5479a0bf528SMauro Carvalho Chehab 	/* wait for WGAGC lock */
5489a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(2000);
5499a0bf528SMauro Carvalho Chehab 	while (time_before(jiffies, timeout)) {
5509a0bf528SMauro Carvalho Chehab 		msleep(10);
5519a0bf528SMauro Carvalho Chehab 		if (stv0297_readreg(state, 0x43) & 0x08)
5529a0bf528SMauro Carvalho Chehab 			break;
5539a0bf528SMauro Carvalho Chehab 	}
5549a0bf528SMauro Carvalho Chehab 	if (time_after(jiffies, timeout)) {
5559a0bf528SMauro Carvalho Chehab 		goto timeout;
5569a0bf528SMauro Carvalho Chehab 	}
5579a0bf528SMauro Carvalho Chehab 	msleep(20);
5589a0bf528SMauro Carvalho Chehab 
5599a0bf528SMauro Carvalho Chehab 	/* wait for equaliser partial convergence */
5609a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(500);
5619a0bf528SMauro Carvalho Chehab 	while (time_before(jiffies, timeout)) {
5629a0bf528SMauro Carvalho Chehab 		msleep(10);
5639a0bf528SMauro Carvalho Chehab 
5649a0bf528SMauro Carvalho Chehab 		if (stv0297_readreg(state, 0x82) & 0x04) {
5659a0bf528SMauro Carvalho Chehab 			break;
5669a0bf528SMauro Carvalho Chehab 		}
5679a0bf528SMauro Carvalho Chehab 	}
5689a0bf528SMauro Carvalho Chehab 	if (time_after(jiffies, timeout)) {
5699a0bf528SMauro Carvalho Chehab 		goto timeout;
5709a0bf528SMauro Carvalho Chehab 	}
5719a0bf528SMauro Carvalho Chehab 
5729a0bf528SMauro Carvalho Chehab 	/* wait for equaliser full convergence */
5739a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(delay);
5749a0bf528SMauro Carvalho Chehab 	while (time_before(jiffies, timeout)) {
5759a0bf528SMauro Carvalho Chehab 		msleep(10);
5769a0bf528SMauro Carvalho Chehab 
5779a0bf528SMauro Carvalho Chehab 		if (stv0297_readreg(state, 0x82) & 0x08) {
5789a0bf528SMauro Carvalho Chehab 			break;
5799a0bf528SMauro Carvalho Chehab 		}
5809a0bf528SMauro Carvalho Chehab 	}
5819a0bf528SMauro Carvalho Chehab 	if (time_after(jiffies, timeout)) {
5829a0bf528SMauro Carvalho Chehab 		goto timeout;
5839a0bf528SMauro Carvalho Chehab 	}
5849a0bf528SMauro Carvalho Chehab 
5859a0bf528SMauro Carvalho Chehab 	/* disable sweep */
5869a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x6a, 1, 0);
5879a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x88, 8, 0);
5889a0bf528SMauro Carvalho Chehab 
5899a0bf528SMauro Carvalho Chehab 	/* wait for main lock */
5909a0bf528SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(20);
5919a0bf528SMauro Carvalho Chehab 	while (time_before(jiffies, timeout)) {
5929a0bf528SMauro Carvalho Chehab 		msleep(10);
5939a0bf528SMauro Carvalho Chehab 
5949a0bf528SMauro Carvalho Chehab 		if (stv0297_readreg(state, 0xDF) & 0x80) {
5959a0bf528SMauro Carvalho Chehab 			break;
5969a0bf528SMauro Carvalho Chehab 		}
5979a0bf528SMauro Carvalho Chehab 	}
5989a0bf528SMauro Carvalho Chehab 	if (time_after(jiffies, timeout)) {
5999a0bf528SMauro Carvalho Chehab 		goto timeout;
6009a0bf528SMauro Carvalho Chehab 	}
6019a0bf528SMauro Carvalho Chehab 	msleep(100);
6029a0bf528SMauro Carvalho Chehab 
6039a0bf528SMauro Carvalho Chehab 	/* is it still locked after that delay? */
6049a0bf528SMauro Carvalho Chehab 	if (!(stv0297_readreg(state, 0xDF) & 0x80)) {
6059a0bf528SMauro Carvalho Chehab 		goto timeout;
6069a0bf528SMauro Carvalho Chehab 	}
6079a0bf528SMauro Carvalho Chehab 
6089a0bf528SMauro Carvalho Chehab 	/* success!! */
6099a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x5a, 0x40, 0x00);
6109a0bf528SMauro Carvalho Chehab 	state->base_freq = p->frequency;
6119a0bf528SMauro Carvalho Chehab 	return 0;
6129a0bf528SMauro Carvalho Chehab 
6139a0bf528SMauro Carvalho Chehab timeout:
6149a0bf528SMauro Carvalho Chehab 	stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
6159a0bf528SMauro Carvalho Chehab 	return 0;
6169a0bf528SMauro Carvalho Chehab }
6179a0bf528SMauro Carvalho Chehab 
6187e3e68bcSMauro Carvalho Chehab static int stv0297_get_frontend(struct dvb_frontend *fe,
6197e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *p)
6209a0bf528SMauro Carvalho Chehab {
6219a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
6229a0bf528SMauro Carvalho Chehab 	int reg_00, reg_83;
6239a0bf528SMauro Carvalho Chehab 
6249a0bf528SMauro Carvalho Chehab 	reg_00 = stv0297_readreg(state, 0x00);
6259a0bf528SMauro Carvalho Chehab 	reg_83 = stv0297_readreg(state, 0x83);
6269a0bf528SMauro Carvalho Chehab 
6279a0bf528SMauro Carvalho Chehab 	p->frequency = state->base_freq;
6289a0bf528SMauro Carvalho Chehab 	p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF;
6299a0bf528SMauro Carvalho Chehab 	if (state->config->invert)
6309a0bf528SMauro Carvalho Chehab 		p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
6319a0bf528SMauro Carvalho Chehab 	p->symbol_rate = stv0297_get_symbolrate(state) * 1000;
6329a0bf528SMauro Carvalho Chehab 	p->fec_inner = FEC_NONE;
6339a0bf528SMauro Carvalho Chehab 
6349a0bf528SMauro Carvalho Chehab 	switch ((reg_00 >> 4) & 0x7) {
6359a0bf528SMauro Carvalho Chehab 	case 0:
6369a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_16;
6379a0bf528SMauro Carvalho Chehab 		break;
6389a0bf528SMauro Carvalho Chehab 	case 1:
6399a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_32;
6409a0bf528SMauro Carvalho Chehab 		break;
6419a0bf528SMauro Carvalho Chehab 	case 2:
6429a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_128;
6439a0bf528SMauro Carvalho Chehab 		break;
6449a0bf528SMauro Carvalho Chehab 	case 3:
6459a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_256;
6469a0bf528SMauro Carvalho Chehab 		break;
6479a0bf528SMauro Carvalho Chehab 	case 4:
6489a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_64;
6499a0bf528SMauro Carvalho Chehab 		break;
6509a0bf528SMauro Carvalho Chehab 	}
6519a0bf528SMauro Carvalho Chehab 
6529a0bf528SMauro Carvalho Chehab 	return 0;
6539a0bf528SMauro Carvalho Chehab }
6549a0bf528SMauro Carvalho Chehab 
6559a0bf528SMauro Carvalho Chehab static void stv0297_release(struct dvb_frontend *fe)
6569a0bf528SMauro Carvalho Chehab {
6579a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = fe->demodulator_priv;
6589a0bf528SMauro Carvalho Chehab 	kfree(state);
6599a0bf528SMauro Carvalho Chehab }
6609a0bf528SMauro Carvalho Chehab 
6619a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops stv0297_ops;
6629a0bf528SMauro Carvalho Chehab 
6639a0bf528SMauro Carvalho Chehab struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
6649a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
6659a0bf528SMauro Carvalho Chehab {
6669a0bf528SMauro Carvalho Chehab 	struct stv0297_state *state = NULL;
6679a0bf528SMauro Carvalho Chehab 
6689a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
6699a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL);
6709a0bf528SMauro Carvalho Chehab 	if (state == NULL)
6719a0bf528SMauro Carvalho Chehab 		goto error;
6729a0bf528SMauro Carvalho Chehab 
6739a0bf528SMauro Carvalho Chehab 	/* setup the state */
6749a0bf528SMauro Carvalho Chehab 	state->config = config;
6759a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
6769a0bf528SMauro Carvalho Chehab 	state->last_ber = 0;
6779a0bf528SMauro Carvalho Chehab 	state->base_freq = 0;
6789a0bf528SMauro Carvalho Chehab 
6799a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
6809a0bf528SMauro Carvalho Chehab 	if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20)
6819a0bf528SMauro Carvalho Chehab 		goto error;
6829a0bf528SMauro Carvalho Chehab 
6839a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
6849a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &stv0297_ops, sizeof(struct dvb_frontend_ops));
6859a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
6869a0bf528SMauro Carvalho Chehab 	return &state->frontend;
6879a0bf528SMauro Carvalho Chehab 
6889a0bf528SMauro Carvalho Chehab error:
6899a0bf528SMauro Carvalho Chehab 	kfree(state);
6909a0bf528SMauro Carvalho Chehab 	return NULL;
6919a0bf528SMauro Carvalho Chehab }
6929a0bf528SMauro Carvalho Chehab 
6939a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops stv0297_ops = {
6949a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBC_ANNEX_A },
6959a0bf528SMauro Carvalho Chehab 	.info = {
6969a0bf528SMauro Carvalho Chehab 		 .name = "ST STV0297 DVB-C",
6979a0bf528SMauro Carvalho Chehab 		 .frequency_min = 47000000,
6989a0bf528SMauro Carvalho Chehab 		 .frequency_max = 862000000,
6999a0bf528SMauro Carvalho Chehab 		 .frequency_stepsize = 62500,
7009a0bf528SMauro Carvalho Chehab 		 .symbol_rate_min = 870000,
7019a0bf528SMauro Carvalho Chehab 		 .symbol_rate_max = 11700000,
7029a0bf528SMauro Carvalho Chehab 		 .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
7039a0bf528SMauro Carvalho Chehab 		 FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
7049a0bf528SMauro Carvalho Chehab 
7059a0bf528SMauro Carvalho Chehab 	.release = stv0297_release,
7069a0bf528SMauro Carvalho Chehab 
7079a0bf528SMauro Carvalho Chehab 	.init = stv0297_init,
7089a0bf528SMauro Carvalho Chehab 	.sleep = stv0297_sleep,
7099a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl = stv0297_i2c_gate_ctrl,
7109a0bf528SMauro Carvalho Chehab 
7119a0bf528SMauro Carvalho Chehab 	.set_frontend = stv0297_set_frontend,
7129a0bf528SMauro Carvalho Chehab 	.get_frontend = stv0297_get_frontend,
7139a0bf528SMauro Carvalho Chehab 
7149a0bf528SMauro Carvalho Chehab 	.read_status = stv0297_read_status,
7159a0bf528SMauro Carvalho Chehab 	.read_ber = stv0297_read_ber,
7169a0bf528SMauro Carvalho Chehab 	.read_signal_strength = stv0297_read_signal_strength,
7179a0bf528SMauro Carvalho Chehab 	.read_snr = stv0297_read_snr,
7189a0bf528SMauro Carvalho Chehab 	.read_ucblocks = stv0297_read_ucblocks,
7199a0bf528SMauro Carvalho Chehab };
7209a0bf528SMauro Carvalho Chehab 
7219a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver");
7229a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey");
7239a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
7249a0bf528SMauro Carvalho Chehab 
7259a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(stv0297_attach);
726