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