blob: 9df14d0be1c1a4e10f5d471dbb4911a9b8630630 [file] [log] [blame]
Thomas Gleixner74ba9202019-05-20 09:19:02 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 VES1820 - Single Chip Cable Channel Receiver driver module
4
5 Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
6
Linus Torvalds1da177e2005-04-16 15:20:36 -07007*/
8
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/delay.h>
10#include <linux/errno.h>
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/string.h>
15#include <linux/slab.h>
16#include <asm/div64.h>
17
Mauro Carvalho Chehabfada1932017-12-28 13:03:51 -050018#include <media/dvb_frontend.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include "ves1820.h"
20
21
22
23struct ves1820_state {
24 struct i2c_adapter* i2c;
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 /* configuration settings */
26 const struct ves1820_config* config;
27 struct dvb_frontend frontend;
28
29 /* private demodulator data */
30 u8 reg0;
31 u8 pwm;
32};
33
34
35static int verbose;
36
37static u8 ves1820_inittab[] = {
Oliver Endriss4a3625b2007-10-31 01:34:25 -030038 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
40 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
41 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
42 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00, 0x00, 0x00, 0x40
45};
46
47static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
48{
49 u8 buf[] = { 0x00, reg, data };
50 struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
51 int ret;
52
53 ret = i2c_transfer(state->i2c, &msg, 1);
54
55 if (ret != 1)
Mauro Carvalho Chehab4bd69e72016-10-18 17:44:22 -020056 printk("ves1820: %s(): writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
57 __func__, reg, data, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 return (ret != 1) ? -EREMOTEIO : 0;
60}
61
62static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
63{
64 u8 b0[] = { 0x00, reg };
65 u8 b1[] = { 0 };
66 struct i2c_msg msg[] = {
67 {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
68 {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
69 };
70 int ret;
71
72 ret = i2c_transfer(state->i2c, msg, 2);
73
74 if (ret != 2)
Mauro Carvalho Chehab4bd69e72016-10-18 17:44:22 -020075 printk("ves1820: %s(): readreg error (reg == 0x%02x, ret == %i)\n",
76 __func__, reg, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78 return b1[0];
79}
80
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -030081static int ves1820_setup_reg0(struct ves1820_state *state,
82 u8 reg0, enum fe_spectral_inversion inversion)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083{
84 reg0 |= state->reg0 & 0x62;
85
86 if (INVERSION_ON == inversion) {
87 if (!state->config->invert) reg0 |= 0x20;
88 else reg0 &= ~0x20;
89 } else if (INVERSION_OFF == inversion) {
90 if (!state->config->invert) reg0 &= ~0x20;
91 else reg0 |= 0x20;
92 }
93
94 ves1820_writereg(state, 0x00, reg0 & 0xfe);
95 ves1820_writereg(state, 0x00, reg0 | 0x01);
96
97 state->reg0 = reg0;
98
99 return 0;
100}
101
102static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
103{
104 s32 BDR;
105 s32 BDRI;
106 s16 SFIL = 0;
107 u16 NDEC = 0;
108 u32 ratio;
109 u32 fin;
110 u32 tmp;
111 u64 fptmp;
112 u64 fpxin;
113
114 if (symbolrate > state->config->xin / 2)
115 symbolrate = state->config->xin / 2;
116
117 if (symbolrate < 500000)
118 symbolrate = 500000;
119
120 if (symbolrate < state->config->xin / 16)
121 NDEC = 1;
122 if (symbolrate < state->config->xin / 32)
123 NDEC = 2;
124 if (symbolrate < state->config->xin / 64)
125 NDEC = 3;
126
127 /* yeuch! */
Gustavo A. R. Silva91e0c0c2018-02-06 11:47:09 -0500128 fpxin = state->config->xin * 10ULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 fptmp = fpxin; do_div(fptmp, 123);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800130 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 SFIL = 1;
132 fptmp = fpxin; do_div(fptmp, 160);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800133 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 SFIL = 0;
135 fptmp = fpxin; do_div(fptmp, 246);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800136 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 SFIL = 1;
138 fptmp = fpxin; do_div(fptmp, 320);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800139 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 SFIL = 0;
141 fptmp = fpxin; do_div(fptmp, 492);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800142 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 SFIL = 1;
144 fptmp = fpxin; do_div(fptmp, 640);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800145 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 SFIL = 0;
147 fptmp = fpxin; do_div(fptmp, 984);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800148 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 SFIL = 1;
150
151 fin = state->config->xin >> 4;
152 symbolrate <<= NDEC;
153 ratio = (symbolrate << 4) / fin;
154 tmp = ((symbolrate << 4) % fin) << 8;
155 ratio = (ratio << 8) + tmp / fin;
156 tmp = (tmp % fin) << 8;
Julia Lawall75b697f2009-08-01 16:48:41 -0300157 ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 BDR = ratio;
160 BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
161
162 if (BDRI > 0xFF)
163 BDRI = 0xFF;
164
165 SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
166
167 NDEC = (NDEC << 6) | ves1820_inittab[0x03];
168
169 ves1820_writereg(state, 0x03, NDEC);
170 ves1820_writereg(state, 0x0a, BDR & 0xff);
171 ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
172 ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
173
174 ves1820_writereg(state, 0x0d, BDRI);
175 ves1820_writereg(state, 0x0e, SFIL);
176
177 return 0;
178}
179
180static int ves1820_init(struct dvb_frontend* fe)
181{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700182 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185 ves1820_writereg(state, 0, 0);
186
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700187 for (i = 0; i < sizeof(ves1820_inittab); i++)
188 ves1820_writereg(state, i, ves1820_inittab[i]);
189 if (state->config->selagc)
190 ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192 ves1820_writereg(state, 0x34, state->pwm);
193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 return 0;
195}
196
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300197static int ves1820_set_parameters(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300199 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700200 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
202 static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
203 static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
204 static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
205 static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300206 int real_qam = p->modulation - QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
208 if (real_qam < 0 || real_qam > 4)
209 return -EINVAL;
210
Patrick Boettcherdea74862006-05-14 05:01:31 -0300211 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300212 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherdea74862006-05-14 05:01:31 -0300213 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
Andrew de Quincey58b119e2006-04-18 17:47:10 -0300214 }
215
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300216 ves1820_set_symbolrate(state, p->symbol_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 ves1820_writereg(state, 0x34, state->pwm);
218
219 ves1820_writereg(state, 0x01, reg0x01[real_qam]);
220 ves1820_writereg(state, 0x05, reg0x05[real_qam]);
221 ves1820_writereg(state, 0x08, reg0x08[real_qam]);
222 ves1820_writereg(state, 0x09, reg0x09[real_qam]);
223
224 ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700225 ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 return 0;
227}
228
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -0300229static int ves1820_read_status(struct dvb_frontend *fe,
230 enum fe_status *status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700232 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 int sync;
234
235 *status = 0;
236 sync = ves1820_readreg(state, 0x11);
237
238 if (sync & 1)
239 *status |= FE_HAS_SIGNAL;
240
241 if (sync & 2)
242 *status |= FE_HAS_CARRIER;
243
244 if (sync & 2) /* XXX FIXME! */
245 *status |= FE_HAS_VITERBI;
246
247 if (sync & 4)
248 *status |= FE_HAS_SYNC;
249
250 if (sync & 8)
251 *status |= FE_HAS_LOCK;
252
253 return 0;
254}
255
256static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
257{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700258 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 u32 _ber = ves1820_readreg(state, 0x14) |
261 (ves1820_readreg(state, 0x15) << 8) |
262 ((ves1820_readreg(state, 0x16) & 0x0f) << 16);
263 *ber = 10 * _ber;
264
265 return 0;
266}
267
268static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
269{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700270 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 u8 gain = ves1820_readreg(state, 0x17);
273 *strength = (gain << 8) | gain;
274
275 return 0;
276}
277
278static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
279{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700280 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282 u8 quality = ~ves1820_readreg(state, 0x18);
283 *snr = (quality << 8) | quality;
284
285 return 0;
286}
287
288static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
289{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700290 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 *ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
293 if (*ucblocks == 0x7f)
294 *ucblocks = 0xffffffff;
295
296 /* reset uncorrected block counter */
297 ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
298 ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
299
300 return 0;
301}
302
Mauro Carvalho Chehab7e3e68b2016-02-04 12:58:30 -0200303static int ves1820_get_frontend(struct dvb_frontend *fe,
304 struct dtv_frontend_properties *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700306 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 int sync;
308 s8 afc = 0;
309
310 sync = ves1820_readreg(state, 0x11);
311 afc = ves1820_readreg(state, 0x19);
312 if (verbose) {
313 /* AFC only valid when carrier has been recovered */
314 printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300315 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317
318 if (!state->config->invert) {
319 p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
320 } else {
321 p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
322 }
323
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300324 p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300326 p->fec_inner = FEC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
329 if (sync & 2)
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300330 p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332 return 0;
333}
334
335static int ves1820_sleep(struct dvb_frontend* fe)
336{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700337 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339 ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
340 ves1820_writereg(state, 0x00, 0x80); /* standby */
341
342 return 0;
343}
344
345static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
346{
347
348 fesettings->min_delay_ms = 200;
349 fesettings->step_size = 0;
350 fesettings->max_drift = 0;
351 return 0;
352}
353
354static void ves1820_release(struct dvb_frontend* fe)
355{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700356 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 kfree(state);
358}
359
Max Kellermannbd336e62016-08-09 18:32:21 -0300360static const struct dvb_frontend_ops ves1820_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
363 struct i2c_adapter* i2c,
364 u8 pwm)
365{
366 struct ves1820_state* state = NULL;
367
368 /* allocate memory for the internal state */
Matthias Schwarzott084e24a2009-08-10 22:51:01 -0300369 state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 if (state == NULL)
371 goto error;
372
373 /* setup the state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 state->reg0 = ves1820_inittab[0];
375 state->config = config;
376 state->i2c = i2c;
377 state->pwm = pwm;
378
379 /* check if the demod is there */
380 if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
381 goto error;
382
383 if (verbose)
384 printk("ves1820: pwm=0x%02x\n", state->pwm);
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 /* create dvb_frontend */
Patrick Boettcherdea74862006-05-14 05:01:31 -0300387 memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
388 state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
389 state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 state->frontend.demodulator_priv = state;
Patrick Boettcherdea74862006-05-14 05:01:31 -0300391
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 return &state->frontend;
393
394error:
395 kfree(state);
396 return NULL;
397}
398
Max Kellermannbd336e62016-08-09 18:32:21 -0300399static const struct dvb_frontend_ops ves1820_ops = {
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300400 .delsys = { SYS_DVBC_ANNEX_A },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 .info = {
402 .name = "VLSI VES1820 DVB-C",
Mauro Carvalho Chehabf1b1eab2018-07-05 18:59:36 -0400403 .frequency_min_hz = 47 * MHz,
404 .frequency_max_hz = 862 * MHz,
405 .frequency_stepsize_hz = 62500,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 .caps = FE_CAN_QAM_16 |
407 FE_CAN_QAM_32 |
408 FE_CAN_QAM_64 |
409 FE_CAN_QAM_128 |
410 FE_CAN_QAM_256 |
411 FE_CAN_FEC_AUTO
412 },
413
414 .release = ves1820_release,
415
416 .init = ves1820_init,
417 .sleep = ves1820_sleep,
418
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300419 .set_frontend = ves1820_set_parameters,
420 .get_frontend = ves1820_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 .get_tune_settings = ves1820_get_tune_settings,
422
423 .read_status = ves1820_read_status,
424 .read_ber = ves1820_read_ber,
425 .read_signal_strength = ves1820_read_signal_strength,
426 .read_snr = ves1820_read_snr,
427 .read_ucblocks = ves1820_read_ucblocks,
428};
429
430module_param(verbose, int, 0644);
431MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
432
433MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
434MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
435MODULE_LICENSE("GPL");
436
437EXPORT_SYMBOL(ves1820_attach);