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