Merge tag 'libnvdimm-fixes-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / drivers / media / dvb-frontends / ves1820.c
CommitLineData
74ba9207 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4
LT
2/*
3 VES1820 - Single Chip Cable Channel Receiver driver module
4
5 Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
6
1da177e4
LT
7*/
8
1da177e4
LT
9#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
fada1935 18#include <media/dvb_frontend.h>
1da177e4
LT
19#include "ves1820.h"
20
21
22
23struct ves1820_state {
24 struct i2c_adapter* i2c;
1da177e4
LT
25 /* 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[] = {
4a3625b2 38 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A,
1da177e4
LT
39 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)
4bd69e7b
MCC
56 printk("ves1820: %s(): writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
57 __func__, reg, data, ret);
1da177e4 58
1da177e4
LT
59 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)
4bd69e7b
MCC
75 printk("ves1820: %s(): readreg error (reg == 0x%02x, ret == %i)\n",
76 __func__, reg, ret);
1da177e4
LT
77
78 return b1[0];
79}
80
0df289a2
MCC
81static int ves1820_setup_reg0(struct ves1820_state *state,
82 u8 reg0, enum fe_spectral_inversion inversion)
1da177e4
LT
83{
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! */
91e0c0c1 128 fpxin = state->config->xin * 10ULL;
1da177e4 129 fptmp = fpxin; do_div(fptmp, 123);
48063a75 130 if (symbolrate < fptmp)
1da177e4
LT
131 SFIL = 1;
132 fptmp = fpxin; do_div(fptmp, 160);
48063a75 133 if (symbolrate < fptmp)
1da177e4
LT
134 SFIL = 0;
135 fptmp = fpxin; do_div(fptmp, 246);
48063a75 136 if (symbolrate < fptmp)
1da177e4
LT
137 SFIL = 1;
138 fptmp = fpxin; do_div(fptmp, 320);
48063a75 139 if (symbolrate < fptmp)
1da177e4
LT
140 SFIL = 0;
141 fptmp = fpxin; do_div(fptmp, 492);
48063a75 142 if (symbolrate < fptmp)
1da177e4
LT
143 SFIL = 1;
144 fptmp = fpxin; do_div(fptmp, 640);
48063a75 145 if (symbolrate < fptmp)
1da177e4
LT
146 SFIL = 0;
147 fptmp = fpxin; do_div(fptmp, 984);
48063a75 148 if (symbolrate < fptmp)
1da177e4
LT
149 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;
75b697f7 157 ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
1da177e4
LT
158
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{
b8742700 182 struct ves1820_state* state = fe->demodulator_priv;
1da177e4 183 int i;
1da177e4
LT
184
185 ves1820_writereg(state, 0, 0);
186
6816a4c1
JS
187 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);
1da177e4
LT
191
192 ves1820_writereg(state, 0x34, state->pwm);
193
1da177e4
LT
194 return 0;
195}
196
f6c69968 197static int ves1820_set_parameters(struct dvb_frontend *fe)
1da177e4 198{
f6c69968 199 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
b8742700 200 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
201 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 };
f6c69968 206 int real_qam = p->modulation - QAM_16;
1da177e4
LT
207
208 if (real_qam < 0 || real_qam > 4)
209 return -EINVAL;
210
dea74869 211 if (fe->ops.tuner_ops.set_params) {
14d24d14 212 fe->ops.tuner_ops.set_params(fe);
dea74869 213 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
58b119e8
AQ
214 }
215
f6c69968 216 ves1820_set_symbolrate(state, p->symbol_rate);
1da177e4
LT
217 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);
6816a4c1 225 ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
1da177e4
LT
226 return 0;
227}
228
0df289a2
MCC
229static int ves1820_read_status(struct dvb_frontend *fe,
230 enum fe_status *status)
1da177e4 231{
b8742700 232 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
233 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{
b8742700 258 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
259
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{
b8742700 270 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
271
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{
b8742700 280 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
281
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{
b8742700 290 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
291
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
7e3e68bc
MCC
303static int ves1820_get_frontend(struct dvb_frontend *fe,
304 struct dtv_frontend_properties *p)
1da177e4 305{
b8742700 306 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
307 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" :
f6c69968 315 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
1da177e4
LT
316 }
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
f6c69968 324 p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
1da177e4 325
f6c69968 326 p->fec_inner = FEC_NONE;
1da177e4
LT
327
328 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
329 if (sync & 2)
f6c69968 330 p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
1da177e4
LT
331
332 return 0;
333}
334
335static int ves1820_sleep(struct dvb_frontend* fe)
336{
b8742700 337 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
338
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{
b8742700 356 struct ves1820_state* state = fe->demodulator_priv;
1da177e4
LT
357 kfree(state);
358}
359
bd336e63 360static const struct dvb_frontend_ops ves1820_ops;
1da177e4
LT
361
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 */
084e24ac 369 state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
1da177e4
LT
370 if (state == NULL)
371 goto error;
372
373 /* setup the state */
1da177e4
LT
374 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
1da177e4 386 /* create dvb_frontend */
dea74869
PB
387 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 */
1da177e4 390 state->frontend.demodulator_priv = state;
dea74869 391
1da177e4
LT
392 return &state->frontend;
393
394error:
395 kfree(state);
396 return NULL;
397}
398
bd336e63 399static const struct dvb_frontend_ops ves1820_ops = {
f6c69968 400 .delsys = { SYS_DVBC_ANNEX_A },
1da177e4
LT
401 .info = {
402 .name = "VLSI VES1820 DVB-C",
f1b1eabf
MCC
403 .frequency_min_hz = 47 * MHz,
404 .frequency_max_hz = 862 * MHz,
405 .frequency_stepsize_hz = 62500,
1da177e4
LT
406 .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
f6c69968
MCC
419 .set_frontend = ves1820_set_parameters,
420 .get_frontend = ves1820_get_frontend,
1da177e4
LT
421 .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);