19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     VES1820  - Single Chip Cable Channel Receiver driver module
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
59a0bf528SMauro Carvalho Chehab 
69a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
79a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
89a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
99a0bf528SMauro Carvalho Chehab     (at your option) any later version.
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
129a0bf528SMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
139a0bf528SMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
159a0bf528SMauro Carvalho Chehab 
169a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
179a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
189a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
199a0bf528SMauro Carvalho Chehab */
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab #include <linux/delay.h>
229a0bf528SMauro Carvalho Chehab #include <linux/errno.h>
239a0bf528SMauro Carvalho Chehab #include <linux/init.h>
249a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
259a0bf528SMauro Carvalho Chehab #include <linux/module.h>
269a0bf528SMauro Carvalho Chehab #include <linux/string.h>
279a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
289a0bf528SMauro Carvalho Chehab #include <asm/div64.h>
299a0bf528SMauro Carvalho Chehab 
30fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
319a0bf528SMauro Carvalho Chehab #include "ves1820.h"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab struct ves1820_state {
369a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
379a0bf528SMauro Carvalho Chehab 	/* configuration settings */
389a0bf528SMauro Carvalho Chehab 	const struct ves1820_config* config;
399a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
409a0bf528SMauro Carvalho Chehab 
419a0bf528SMauro Carvalho Chehab 	/* private demodulator data */
429a0bf528SMauro Carvalho Chehab 	u8 reg0;
439a0bf528SMauro Carvalho Chehab 	u8 pwm;
449a0bf528SMauro Carvalho Chehab };
459a0bf528SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static int verbose;
489a0bf528SMauro Carvalho Chehab 
499a0bf528SMauro Carvalho Chehab static u8 ves1820_inittab[] = {
509a0bf528SMauro Carvalho Chehab 	0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A,
519a0bf528SMauro Carvalho Chehab 	0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
529a0bf528SMauro Carvalho Chehab 	0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
539a0bf528SMauro Carvalho Chehab 	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
549a0bf528SMauro Carvalho Chehab 	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559a0bf528SMauro Carvalho Chehab 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569a0bf528SMauro Carvalho Chehab 	0x00, 0x00, 0x00, 0x00, 0x40
579a0bf528SMauro Carvalho Chehab };
589a0bf528SMauro Carvalho Chehab 
599a0bf528SMauro Carvalho Chehab static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
609a0bf528SMauro Carvalho Chehab {
619a0bf528SMauro Carvalho Chehab 	u8 buf[] = { 0x00, reg, data };
629a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
639a0bf528SMauro Carvalho Chehab 	int ret;
649a0bf528SMauro Carvalho Chehab 
659a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
669a0bf528SMauro Carvalho Chehab 
679a0bf528SMauro Carvalho Chehab 	if (ret != 1)
684bd69e7bSMauro Carvalho Chehab 		printk("ves1820: %s(): writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
694bd69e7bSMauro Carvalho Chehab 		       __func__, reg, data, ret);
709a0bf528SMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -EREMOTEIO : 0;
729a0bf528SMauro Carvalho Chehab }
739a0bf528SMauro Carvalho Chehab 
749a0bf528SMauro Carvalho Chehab static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
759a0bf528SMauro Carvalho Chehab {
769a0bf528SMauro Carvalho Chehab 	u8 b0[] = { 0x00, reg };
779a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0 };
789a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
799a0bf528SMauro Carvalho Chehab 		{.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
809a0bf528SMauro Carvalho Chehab 		{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
819a0bf528SMauro Carvalho Chehab 	};
829a0bf528SMauro Carvalho Chehab 	int ret;
839a0bf528SMauro Carvalho Chehab 
849a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
859a0bf528SMauro Carvalho Chehab 
869a0bf528SMauro Carvalho Chehab 	if (ret != 2)
874bd69e7bSMauro Carvalho Chehab 		printk("ves1820: %s(): readreg error (reg == 0x%02x, ret == %i)\n",
884bd69e7bSMauro Carvalho Chehab 		       __func__, reg, ret);
899a0bf528SMauro Carvalho Chehab 
909a0bf528SMauro Carvalho Chehab 	return b1[0];
919a0bf528SMauro Carvalho Chehab }
929a0bf528SMauro Carvalho Chehab 
930df289a2SMauro Carvalho Chehab static int ves1820_setup_reg0(struct ves1820_state *state,
940df289a2SMauro Carvalho Chehab 			      u8 reg0, enum fe_spectral_inversion inversion)
959a0bf528SMauro Carvalho Chehab {
969a0bf528SMauro Carvalho Chehab 	reg0 |= state->reg0 & 0x62;
979a0bf528SMauro Carvalho Chehab 
989a0bf528SMauro Carvalho Chehab 	if (INVERSION_ON == inversion) {
999a0bf528SMauro Carvalho Chehab 		if (!state->config->invert) reg0 |= 0x20;
1009a0bf528SMauro Carvalho Chehab 		else reg0 &= ~0x20;
1019a0bf528SMauro Carvalho Chehab 	} else if (INVERSION_OFF == inversion) {
1029a0bf528SMauro Carvalho Chehab 		if (!state->config->invert) reg0 &= ~0x20;
1039a0bf528SMauro Carvalho Chehab 		else reg0 |= 0x20;
1049a0bf528SMauro Carvalho Chehab 	}
1059a0bf528SMauro Carvalho Chehab 
1069a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x00, reg0 & 0xfe);
1079a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x00, reg0 | 0x01);
1089a0bf528SMauro Carvalho Chehab 
1099a0bf528SMauro Carvalho Chehab 	state->reg0 = reg0;
1109a0bf528SMauro Carvalho Chehab 
1119a0bf528SMauro Carvalho Chehab 	return 0;
1129a0bf528SMauro Carvalho Chehab }
1139a0bf528SMauro Carvalho Chehab 
1149a0bf528SMauro Carvalho Chehab static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
1159a0bf528SMauro Carvalho Chehab {
1169a0bf528SMauro Carvalho Chehab 	s32 BDR;
1179a0bf528SMauro Carvalho Chehab 	s32 BDRI;
1189a0bf528SMauro Carvalho Chehab 	s16 SFIL = 0;
1199a0bf528SMauro Carvalho Chehab 	u16 NDEC = 0;
1209a0bf528SMauro Carvalho Chehab 	u32 ratio;
1219a0bf528SMauro Carvalho Chehab 	u32 fin;
1229a0bf528SMauro Carvalho Chehab 	u32 tmp;
1239a0bf528SMauro Carvalho Chehab 	u64 fptmp;
1249a0bf528SMauro Carvalho Chehab 	u64 fpxin;
1259a0bf528SMauro Carvalho Chehab 
1269a0bf528SMauro Carvalho Chehab 	if (symbolrate > state->config->xin / 2)
1279a0bf528SMauro Carvalho Chehab 		symbolrate = state->config->xin / 2;
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab 	if (symbolrate < 500000)
1309a0bf528SMauro Carvalho Chehab 		symbolrate = 500000;
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 	if (symbolrate < state->config->xin / 16)
1339a0bf528SMauro Carvalho Chehab 		NDEC = 1;
1349a0bf528SMauro Carvalho Chehab 	if (symbolrate < state->config->xin / 32)
1359a0bf528SMauro Carvalho Chehab 		NDEC = 2;
1369a0bf528SMauro Carvalho Chehab 	if (symbolrate < state->config->xin / 64)
1379a0bf528SMauro Carvalho Chehab 		NDEC = 3;
1389a0bf528SMauro Carvalho Chehab 
1399a0bf528SMauro Carvalho Chehab 	/* yeuch! */
14091e0c0c1SGustavo A. R. Silva 	fpxin = state->config->xin * 10ULL;
1419a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 123);
1429a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1439a0bf528SMauro Carvalho Chehab 		SFIL = 1;
1449a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 160);
1459a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1469a0bf528SMauro Carvalho Chehab 		SFIL = 0;
1479a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 246);
1489a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1499a0bf528SMauro Carvalho Chehab 		SFIL = 1;
1509a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 320);
1519a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1529a0bf528SMauro Carvalho Chehab 		SFIL = 0;
1539a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 492);
1549a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1559a0bf528SMauro Carvalho Chehab 		SFIL = 1;
1569a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 640);
1579a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1589a0bf528SMauro Carvalho Chehab 		SFIL = 0;
1599a0bf528SMauro Carvalho Chehab 	fptmp = fpxin; do_div(fptmp, 984);
1609a0bf528SMauro Carvalho Chehab 	if (symbolrate < fptmp)
1619a0bf528SMauro Carvalho Chehab 		SFIL = 1;
1629a0bf528SMauro Carvalho Chehab 
1639a0bf528SMauro Carvalho Chehab 	fin = state->config->xin >> 4;
1649a0bf528SMauro Carvalho Chehab 	symbolrate <<= NDEC;
1659a0bf528SMauro Carvalho Chehab 	ratio = (symbolrate << 4) / fin;
1669a0bf528SMauro Carvalho Chehab 	tmp = ((symbolrate << 4) % fin) << 8;
1679a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 8) + tmp / fin;
1689a0bf528SMauro Carvalho Chehab 	tmp = (tmp % fin) << 8;
1699a0bf528SMauro Carvalho Chehab 	ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
1709a0bf528SMauro Carvalho Chehab 
1719a0bf528SMauro Carvalho Chehab 	BDR = ratio;
1729a0bf528SMauro Carvalho Chehab 	BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
1739a0bf528SMauro Carvalho Chehab 
1749a0bf528SMauro Carvalho Chehab 	if (BDRI > 0xFF)
1759a0bf528SMauro Carvalho Chehab 		BDRI = 0xFF;
1769a0bf528SMauro Carvalho Chehab 
1779a0bf528SMauro Carvalho Chehab 	SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	NDEC = (NDEC << 6) | ves1820_inittab[0x03];
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x03, NDEC);
1829a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x0a, BDR & 0xff);
1839a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
1849a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
1859a0bf528SMauro Carvalho Chehab 
1869a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x0d, BDRI);
1879a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x0e, SFIL);
1889a0bf528SMauro Carvalho Chehab 
1899a0bf528SMauro Carvalho Chehab 	return 0;
1909a0bf528SMauro Carvalho Chehab }
1919a0bf528SMauro Carvalho Chehab 
1929a0bf528SMauro Carvalho Chehab static int ves1820_init(struct dvb_frontend* fe)
1939a0bf528SMauro Carvalho Chehab {
1949a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
1959a0bf528SMauro Carvalho Chehab 	int i;
1969a0bf528SMauro Carvalho Chehab 
1979a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0, 0);
1989a0bf528SMauro Carvalho Chehab 
1999a0bf528SMauro Carvalho Chehab 	for (i = 0; i < sizeof(ves1820_inittab); i++)
2009a0bf528SMauro Carvalho Chehab 		ves1820_writereg(state, i, ves1820_inittab[i]);
2019a0bf528SMauro Carvalho Chehab 	if (state->config->selagc)
2029a0bf528SMauro Carvalho Chehab 		ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08);
2039a0bf528SMauro Carvalho Chehab 
2049a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x34, state->pwm);
2059a0bf528SMauro Carvalho Chehab 
2069a0bf528SMauro Carvalho Chehab 	return 0;
2079a0bf528SMauro Carvalho Chehab }
2089a0bf528SMauro Carvalho Chehab 
2099a0bf528SMauro Carvalho Chehab static int ves1820_set_parameters(struct dvb_frontend *fe)
2109a0bf528SMauro Carvalho Chehab {
2119a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
2129a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
2139a0bf528SMauro Carvalho Chehab 	static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
2149a0bf528SMauro Carvalho Chehab 	static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
2159a0bf528SMauro Carvalho Chehab 	static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
2169a0bf528SMauro Carvalho Chehab 	static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
2179a0bf528SMauro Carvalho Chehab 	static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
2189a0bf528SMauro Carvalho Chehab 	int real_qam = p->modulation - QAM_16;
2199a0bf528SMauro Carvalho Chehab 
2209a0bf528SMauro Carvalho Chehab 	if (real_qam < 0 || real_qam > 4)
2219a0bf528SMauro Carvalho Chehab 		return -EINVAL;
2229a0bf528SMauro Carvalho Chehab 
2239a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
2249a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
2259a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2269a0bf528SMauro Carvalho Chehab 	}
2279a0bf528SMauro Carvalho Chehab 
2289a0bf528SMauro Carvalho Chehab 	ves1820_set_symbolrate(state, p->symbol_rate);
2299a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x34, state->pwm);
2309a0bf528SMauro Carvalho Chehab 
2319a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x01, reg0x01[real_qam]);
2329a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x05, reg0x05[real_qam]);
2339a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x08, reg0x08[real_qam]);
2349a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x09, reg0x09[real_qam]);
2359a0bf528SMauro Carvalho Chehab 
2369a0bf528SMauro Carvalho Chehab 	ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
2379a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
2389a0bf528SMauro Carvalho Chehab 	return 0;
2399a0bf528SMauro Carvalho Chehab }
2409a0bf528SMauro Carvalho Chehab 
2410df289a2SMauro Carvalho Chehab static int ves1820_read_status(struct dvb_frontend *fe,
2420df289a2SMauro Carvalho Chehab 			       enum fe_status *status)
2439a0bf528SMauro Carvalho Chehab {
2449a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
2459a0bf528SMauro Carvalho Chehab 	int sync;
2469a0bf528SMauro Carvalho Chehab 
2479a0bf528SMauro Carvalho Chehab 	*status = 0;
2489a0bf528SMauro Carvalho Chehab 	sync = ves1820_readreg(state, 0x11);
2499a0bf528SMauro Carvalho Chehab 
2509a0bf528SMauro Carvalho Chehab 	if (sync & 1)
2519a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
2529a0bf528SMauro Carvalho Chehab 
2539a0bf528SMauro Carvalho Chehab 	if (sync & 2)
2549a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
2559a0bf528SMauro Carvalho Chehab 
2569a0bf528SMauro Carvalho Chehab 	if (sync & 2)	/* XXX FIXME! */
2579a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
2589a0bf528SMauro Carvalho Chehab 
2599a0bf528SMauro Carvalho Chehab 	if (sync & 4)
2609a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
2619a0bf528SMauro Carvalho Chehab 
2629a0bf528SMauro Carvalho Chehab 	if (sync & 8)
2639a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
2649a0bf528SMauro Carvalho Chehab 
2659a0bf528SMauro Carvalho Chehab 	return 0;
2669a0bf528SMauro Carvalho Chehab }
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
2699a0bf528SMauro Carvalho Chehab {
2709a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
2719a0bf528SMauro Carvalho Chehab 
2729a0bf528SMauro Carvalho Chehab 	u32 _ber = ves1820_readreg(state, 0x14) |
2739a0bf528SMauro Carvalho Chehab 			(ves1820_readreg(state, 0x15) << 8) |
2749a0bf528SMauro Carvalho Chehab 			((ves1820_readreg(state, 0x16) & 0x0f) << 16);
2759a0bf528SMauro Carvalho Chehab 	*ber = 10 * _ber;
2769a0bf528SMauro Carvalho Chehab 
2779a0bf528SMauro Carvalho Chehab 	return 0;
2789a0bf528SMauro Carvalho Chehab }
2799a0bf528SMauro Carvalho Chehab 
2809a0bf528SMauro Carvalho Chehab static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
2819a0bf528SMauro Carvalho Chehab {
2829a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
2839a0bf528SMauro Carvalho Chehab 
2849a0bf528SMauro Carvalho Chehab 	u8 gain = ves1820_readreg(state, 0x17);
2859a0bf528SMauro Carvalho Chehab 	*strength = (gain << 8) | gain;
2869a0bf528SMauro Carvalho Chehab 
2879a0bf528SMauro Carvalho Chehab 	return 0;
2889a0bf528SMauro Carvalho Chehab }
2899a0bf528SMauro Carvalho Chehab 
2909a0bf528SMauro Carvalho Chehab static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
2919a0bf528SMauro Carvalho Chehab {
2929a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
2939a0bf528SMauro Carvalho Chehab 
2949a0bf528SMauro Carvalho Chehab 	u8 quality = ~ves1820_readreg(state, 0x18);
2959a0bf528SMauro Carvalho Chehab 	*snr = (quality << 8) | quality;
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab 	return 0;
2989a0bf528SMauro Carvalho Chehab }
2999a0bf528SMauro Carvalho Chehab 
3009a0bf528SMauro Carvalho Chehab static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
3019a0bf528SMauro Carvalho Chehab {
3029a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
3039a0bf528SMauro Carvalho Chehab 
3049a0bf528SMauro Carvalho Chehab 	*ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
3059a0bf528SMauro Carvalho Chehab 	if (*ucblocks == 0x7f)
3069a0bf528SMauro Carvalho Chehab 		*ucblocks = 0xffffffff;
3079a0bf528SMauro Carvalho Chehab 
3089a0bf528SMauro Carvalho Chehab 	/* reset uncorrected block counter */
3099a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
3109a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
3119a0bf528SMauro Carvalho Chehab 
3129a0bf528SMauro Carvalho Chehab 	return 0;
3139a0bf528SMauro Carvalho Chehab }
3149a0bf528SMauro Carvalho Chehab 
3157e3e68bcSMauro Carvalho Chehab static int ves1820_get_frontend(struct dvb_frontend *fe,
3167e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *p)
3179a0bf528SMauro Carvalho Chehab {
3189a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
3199a0bf528SMauro Carvalho Chehab 	int sync;
3209a0bf528SMauro Carvalho Chehab 	s8 afc = 0;
3219a0bf528SMauro Carvalho Chehab 
3229a0bf528SMauro Carvalho Chehab 	sync = ves1820_readreg(state, 0x11);
3239a0bf528SMauro Carvalho Chehab 	afc = ves1820_readreg(state, 0x19);
3249a0bf528SMauro Carvalho Chehab 	if (verbose) {
3259a0bf528SMauro Carvalho Chehab 		/* AFC only valid when carrier has been recovered */
3269a0bf528SMauro Carvalho Chehab 		printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
3279a0bf528SMauro Carvalho Chehab 			"ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
3289a0bf528SMauro Carvalho Chehab 	}
3299a0bf528SMauro Carvalho Chehab 
3309a0bf528SMauro Carvalho Chehab 	if (!state->config->invert) {
3319a0bf528SMauro Carvalho Chehab 		p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
3329a0bf528SMauro Carvalho Chehab 	} else {
3339a0bf528SMauro Carvalho Chehab 		p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
3349a0bf528SMauro Carvalho Chehab 	}
3359a0bf528SMauro Carvalho Chehab 
3369a0bf528SMauro Carvalho Chehab 	p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab 	p->fec_inner = FEC_NONE;
3399a0bf528SMauro Carvalho Chehab 
3409a0bf528SMauro Carvalho Chehab 	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
3419a0bf528SMauro Carvalho Chehab 	if (sync & 2)
3429a0bf528SMauro Carvalho Chehab 		p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 	return 0;
3459a0bf528SMauro Carvalho Chehab }
3469a0bf528SMauro Carvalho Chehab 
3479a0bf528SMauro Carvalho Chehab static int ves1820_sleep(struct dvb_frontend* fe)
3489a0bf528SMauro Carvalho Chehab {
3499a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
3509a0bf528SMauro Carvalho Chehab 
3519a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x1b, 0x02);	/* pdown ADC */
3529a0bf528SMauro Carvalho Chehab 	ves1820_writereg(state, 0x00, 0x80);	/* standby */
3539a0bf528SMauro Carvalho Chehab 
3549a0bf528SMauro Carvalho Chehab 	return 0;
3559a0bf528SMauro Carvalho Chehab }
3569a0bf528SMauro Carvalho Chehab 
3579a0bf528SMauro Carvalho Chehab static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
3589a0bf528SMauro Carvalho Chehab {
3599a0bf528SMauro Carvalho Chehab 
3609a0bf528SMauro Carvalho Chehab 	fesettings->min_delay_ms = 200;
3619a0bf528SMauro Carvalho Chehab 	fesettings->step_size = 0;
3629a0bf528SMauro Carvalho Chehab 	fesettings->max_drift = 0;
3639a0bf528SMauro Carvalho Chehab 	return 0;
3649a0bf528SMauro Carvalho Chehab }
3659a0bf528SMauro Carvalho Chehab 
3669a0bf528SMauro Carvalho Chehab static void ves1820_release(struct dvb_frontend* fe)
3679a0bf528SMauro Carvalho Chehab {
3689a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = fe->demodulator_priv;
3699a0bf528SMauro Carvalho Chehab 	kfree(state);
3709a0bf528SMauro Carvalho Chehab }
3719a0bf528SMauro Carvalho Chehab 
372bd336e63SMax Kellermann static const struct dvb_frontend_ops ves1820_ops;
3739a0bf528SMauro Carvalho Chehab 
3749a0bf528SMauro Carvalho Chehab struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
3759a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter* i2c,
3769a0bf528SMauro Carvalho Chehab 				    u8 pwm)
3779a0bf528SMauro Carvalho Chehab {
3789a0bf528SMauro Carvalho Chehab 	struct ves1820_state* state = NULL;
3799a0bf528SMauro Carvalho Chehab 
3809a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
3819a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
3829a0bf528SMauro Carvalho Chehab 	if (state == NULL)
3839a0bf528SMauro Carvalho Chehab 		goto error;
3849a0bf528SMauro Carvalho Chehab 
3859a0bf528SMauro Carvalho Chehab 	/* setup the state */
3869a0bf528SMauro Carvalho Chehab 	state->reg0 = ves1820_inittab[0];
3879a0bf528SMauro Carvalho Chehab 	state->config = config;
3889a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
3899a0bf528SMauro Carvalho Chehab 	state->pwm = pwm;
3909a0bf528SMauro Carvalho Chehab 
3919a0bf528SMauro Carvalho Chehab 	/* check if the demod is there */
3929a0bf528SMauro Carvalho Chehab 	if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
3939a0bf528SMauro Carvalho Chehab 		goto error;
3949a0bf528SMauro Carvalho Chehab 
3959a0bf528SMauro Carvalho Chehab 	if (verbose)
3969a0bf528SMauro Carvalho Chehab 		printk("ves1820: pwm=0x%02x\n", state->pwm);
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
3999a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
4009a0bf528SMauro Carvalho Chehab 	state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64;      /* SACLK/64 == (XIN/2)/64 */
4019a0bf528SMauro Carvalho Chehab 	state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4;       /* SACLK/4 */
4029a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
4039a0bf528SMauro Carvalho Chehab 
4049a0bf528SMauro Carvalho Chehab 	return &state->frontend;
4059a0bf528SMauro Carvalho Chehab 
4069a0bf528SMauro Carvalho Chehab error:
4079a0bf528SMauro Carvalho Chehab 	kfree(state);
4089a0bf528SMauro Carvalho Chehab 	return NULL;
4099a0bf528SMauro Carvalho Chehab }
4109a0bf528SMauro Carvalho Chehab 
411bd336e63SMax Kellermann static const struct dvb_frontend_ops ves1820_ops = {
4129a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBC_ANNEX_A },
4139a0bf528SMauro Carvalho Chehab 	.info = {
4149a0bf528SMauro Carvalho Chehab 		.name = "VLSI VES1820 DVB-C",
415f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz =  47 * MHz,
416f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz = 862 * MHz,
417f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 62500,
4189a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_QAM_16 |
4199a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_32 |
4209a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_64 |
4219a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_128 |
4229a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_256 |
4239a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_AUTO
4249a0bf528SMauro Carvalho Chehab 	},
4259a0bf528SMauro Carvalho Chehab 
4269a0bf528SMauro Carvalho Chehab 	.release = ves1820_release,
4279a0bf528SMauro Carvalho Chehab 
4289a0bf528SMauro Carvalho Chehab 	.init = ves1820_init,
4299a0bf528SMauro Carvalho Chehab 	.sleep = ves1820_sleep,
4309a0bf528SMauro Carvalho Chehab 
4319a0bf528SMauro Carvalho Chehab 	.set_frontend = ves1820_set_parameters,
4329a0bf528SMauro Carvalho Chehab 	.get_frontend = ves1820_get_frontend,
4339a0bf528SMauro Carvalho Chehab 	.get_tune_settings = ves1820_get_tune_settings,
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 	.read_status = ves1820_read_status,
4369a0bf528SMauro Carvalho Chehab 	.read_ber = ves1820_read_ber,
4379a0bf528SMauro Carvalho Chehab 	.read_signal_strength = ves1820_read_signal_strength,
4389a0bf528SMauro Carvalho Chehab 	.read_snr = ves1820_read_snr,
4399a0bf528SMauro Carvalho Chehab 	.read_ucblocks = ves1820_read_ucblocks,
4409a0bf528SMauro Carvalho Chehab };
4419a0bf528SMauro Carvalho Chehab 
4429a0bf528SMauro Carvalho Chehab module_param(verbose, int, 0644);
4439a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
4449a0bf528SMauro Carvalho Chehab 
4459a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
4469a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
4479a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
4489a0bf528SMauro Carvalho Chehab 
4499a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(ves1820_attach);
450