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