1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | VES1820 - Single Chip Cable Channel Receiver driver module |
4 | |
5 | Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> |
6 | |
7 | */ |
8 | |
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 | |
18 | #include <media/dvb_frontend.h> |
19 | #include "ves1820.h" |
20 | |
21 | |
22 | |
23 | struct ves1820_state { |
24 | struct i2c_adapter* i2c; |
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 | |
35 | static int verbose; |
36 | |
37 | static u8 ves1820_inittab[] = { |
38 | 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A, |
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 | |
47 | static 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(adap: state->i2c, msgs: &msg, num: 1); |
54 | |
55 | if (ret != 1) |
56 | printk("ves1820: %s(): writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n" , |
57 | __func__, reg, data, ret); |
58 | |
59 | return (ret != 1) ? -EREMOTEIO : 0; |
60 | } |
61 | |
62 | static 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(adap: state->i2c, msgs: msg, num: 2); |
73 | |
74 | if (ret != 2) |
75 | printk("ves1820: %s(): readreg error (reg == 0x%02x, ret == %i)\n" , |
76 | __func__, reg, ret); |
77 | |
78 | return b1[0]; |
79 | } |
80 | |
81 | static int ves1820_setup_reg0(struct ves1820_state *state, |
82 | u8 reg0, enum fe_spectral_inversion inversion) |
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, reg: 0x00, data: reg0 & 0xfe); |
95 | ves1820_writereg(state, reg: 0x00, data: reg0 | 0x01); |
96 | |
97 | state->reg0 = reg0; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static 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! */ |
128 | fpxin = state->config->xin * 10ULL; |
129 | fptmp = fpxin; do_div(fptmp, 123); |
130 | if (symbolrate < fptmp) |
131 | SFIL = 1; |
132 | fptmp = fpxin; do_div(fptmp, 160); |
133 | if (symbolrate < fptmp) |
134 | SFIL = 0; |
135 | fptmp = fpxin; do_div(fptmp, 246); |
136 | if (symbolrate < fptmp) |
137 | SFIL = 1; |
138 | fptmp = fpxin; do_div(fptmp, 320); |
139 | if (symbolrate < fptmp) |
140 | SFIL = 0; |
141 | fptmp = fpxin; do_div(fptmp, 492); |
142 | if (symbolrate < fptmp) |
143 | SFIL = 1; |
144 | fptmp = fpxin; do_div(fptmp, 640); |
145 | if (symbolrate < fptmp) |
146 | SFIL = 0; |
147 | fptmp = fpxin; do_div(fptmp, 984); |
148 | if (symbolrate < fptmp) |
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; |
157 | ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin); |
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, reg: 0x03, data: NDEC); |
170 | ves1820_writereg(state, reg: 0x0a, data: BDR & 0xff); |
171 | ves1820_writereg(state, reg: 0x0b, data: (BDR >> 8) & 0xff); |
172 | ves1820_writereg(state, reg: 0x0c, data: (BDR >> 16) & 0x3f); |
173 | |
174 | ves1820_writereg(state, reg: 0x0d, data: BDRI); |
175 | ves1820_writereg(state, reg: 0x0e, data: SFIL); |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | static int ves1820_init(struct dvb_frontend* fe) |
181 | { |
182 | struct ves1820_state* state = fe->demodulator_priv; |
183 | int i; |
184 | |
185 | ves1820_writereg(state, reg: 0, data: 0); |
186 | |
187 | for (i = 0; i < sizeof(ves1820_inittab); i++) |
188 | ves1820_writereg(state, reg: i, data: ves1820_inittab[i]); |
189 | if (state->config->selagc) |
190 | ves1820_writereg(state, reg: 2, data: ves1820_inittab[2] | 0x08); |
191 | |
192 | ves1820_writereg(state, reg: 0x34, data: state->pwm); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static int ves1820_set_parameters(struct dvb_frontend *fe) |
198 | { |
199 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
200 | struct ves1820_state* state = fe->demodulator_priv; |
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 }; |
206 | int real_qam = p->modulation - QAM_16; |
207 | |
208 | if (real_qam < 0 || real_qam > 4) |
209 | return -EINVAL; |
210 | |
211 | if (fe->ops.tuner_ops.set_params) { |
212 | fe->ops.tuner_ops.set_params(fe); |
213 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); |
214 | } |
215 | |
216 | ves1820_set_symbolrate(state, symbolrate: p->symbol_rate); |
217 | ves1820_writereg(state, reg: 0x34, data: state->pwm); |
218 | |
219 | ves1820_writereg(state, reg: 0x01, data: reg0x01[real_qam]); |
220 | ves1820_writereg(state, reg: 0x05, data: reg0x05[real_qam]); |
221 | ves1820_writereg(state, reg: 0x08, data: reg0x08[real_qam]); |
222 | ves1820_writereg(state, reg: 0x09, data: reg0x09[real_qam]); |
223 | |
224 | ves1820_setup_reg0(state, reg0: reg0x00[real_qam], inversion: p->inversion); |
225 | ves1820_writereg(state, reg: 2, data: ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0)); |
226 | return 0; |
227 | } |
228 | |
229 | static int ves1820_read_status(struct dvb_frontend *fe, |
230 | enum fe_status *status) |
231 | { |
232 | struct ves1820_state* state = fe->demodulator_priv; |
233 | int sync; |
234 | |
235 | *status = 0; |
236 | sync = ves1820_readreg(state, reg: 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 | |
256 | static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) |
257 | { |
258 | struct ves1820_state* state = fe->demodulator_priv; |
259 | |
260 | u32 _ber = ves1820_readreg(state, reg: 0x14) | |
261 | (ves1820_readreg(state, reg: 0x15) << 8) | |
262 | ((ves1820_readreg(state, reg: 0x16) & 0x0f) << 16); |
263 | *ber = 10 * _ber; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) |
269 | { |
270 | struct ves1820_state* state = fe->demodulator_priv; |
271 | |
272 | u8 gain = ves1820_readreg(state, reg: 0x17); |
273 | *strength = (gain << 8) | gain; |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) |
279 | { |
280 | struct ves1820_state* state = fe->demodulator_priv; |
281 | |
282 | u8 quality = ~ves1820_readreg(state, reg: 0x18); |
283 | *snr = (quality << 8) | quality; |
284 | |
285 | return 0; |
286 | } |
287 | |
288 | static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) |
289 | { |
290 | struct ves1820_state* state = fe->demodulator_priv; |
291 | |
292 | *ucblocks = ves1820_readreg(state, reg: 0x13) & 0x7f; |
293 | if (*ucblocks == 0x7f) |
294 | *ucblocks = 0xffffffff; |
295 | |
296 | /* reset uncorrected block counter */ |
297 | ves1820_writereg(state, reg: 0x10, data: ves1820_inittab[0x10] & 0xdf); |
298 | ves1820_writereg(state, reg: 0x10, data: ves1820_inittab[0x10]); |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | static int ves1820_get_frontend(struct dvb_frontend *fe, |
304 | struct dtv_frontend_properties *p) |
305 | { |
306 | struct ves1820_state* state = fe->demodulator_priv; |
307 | int sync; |
308 | s8 afc = 0; |
309 | |
310 | sync = ves1820_readreg(state, reg: 0x11); |
311 | afc = ves1820_readreg(state, reg: 0x19); |
312 | if (verbose) { |
313 | /* AFC only valid when carrier has been recovered */ |
314 | printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" : |
315 | "ves1820: [AFC (%d) %dHz]\n" , afc, -((s32) p->symbol_rate * afc) >> 10); |
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 | |
324 | p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; |
325 | |
326 | p->fec_inner = FEC_NONE; |
327 | |
328 | p->frequency = ((p->frequency + 31250) / 62500) * 62500; |
329 | if (sync & 2) |
330 | p->frequency -= ((s32) p->symbol_rate * afc) >> 10; |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | static int ves1820_sleep(struct dvb_frontend* fe) |
336 | { |
337 | struct ves1820_state* state = fe->demodulator_priv; |
338 | |
339 | ves1820_writereg(state, reg: 0x1b, data: 0x02); /* pdown ADC */ |
340 | ves1820_writereg(state, reg: 0x00, data: 0x80); /* standby */ |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | static 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 | |
354 | static void ves1820_release(struct dvb_frontend* fe) |
355 | { |
356 | struct ves1820_state* state = fe->demodulator_priv; |
357 | kfree(objp: state); |
358 | } |
359 | |
360 | static const struct dvb_frontend_ops ves1820_ops; |
361 | |
362 | struct 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 */ |
369 | state = kzalloc(size: sizeof(struct ves1820_state), GFP_KERNEL); |
370 | if (state == NULL) |
371 | goto error; |
372 | |
373 | /* setup the state */ |
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, reg: 0x1a) & 0xf0) != 0x70) |
381 | goto error; |
382 | |
383 | if (verbose) |
384 | printk("ves1820: pwm=0x%02x\n" , state->pwm); |
385 | |
386 | /* create dvb_frontend */ |
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 */ |
390 | state->frontend.demodulator_priv = state; |
391 | |
392 | return &state->frontend; |
393 | |
394 | error: |
395 | kfree(objp: state); |
396 | return NULL; |
397 | } |
398 | |
399 | static const struct dvb_frontend_ops ves1820_ops = { |
400 | .delsys = { SYS_DVBC_ANNEX_A }, |
401 | .info = { |
402 | .name = "VLSI VES1820 DVB-C" , |
403 | .frequency_min_hz = 47 * MHz, |
404 | .frequency_max_hz = 862 * MHz, |
405 | .frequency_stepsize_hz = 62500, |
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 | |
419 | .set_frontend = ves1820_set_parameters, |
420 | .get_frontend = ves1820_get_frontend, |
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 | |
430 | module_param(verbose, int, 0644); |
431 | MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting" ); |
432 | |
433 | MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver" ); |
434 | MODULE_AUTHOR("Ralph Metzler, Holger Waechtler" ); |
435 | MODULE_LICENSE("GPL" ); |
436 | |
437 | EXPORT_SYMBOL_GPL(ves1820_attach); |
438 | |