blob: a184597f1d9be0002b8f567b245a849abef6553c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 VES1820 - Single Chip Cable Channel Receiver driver module
3
4 Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/delay.h>
22#include <linux/errno.h>
23#include <linux/init.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/string.h>
27#include <linux/slab.h>
28#include <asm/div64.h>
29
30#include "dvb_frontend.h"
31#include "ves1820.h"
32
33
34
35struct ves1820_state {
36 struct i2c_adapter* i2c;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 /* configuration settings */
38 const struct ves1820_config* config;
39 struct dvb_frontend frontend;
40
41 /* private demodulator data */
42 u8 reg0;
43 u8 pwm;
44};
45
46
47static int verbose;
48
49static u8 ves1820_inittab[] = {
Oliver Endriss4a3625b2007-10-31 01:34:25 -030050 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A,
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
52 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
54 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x40
57};
58
59static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
60{
61 u8 buf[] = { 0x00, reg, data };
62 struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
63 int ret;
64
65 ret = i2c_transfer(state->i2c, &msg, 1);
66
67 if (ret != 1)
Joe Perches11645cc2007-11-19 22:48:15 -030068 printk("ves1820: %s(): writereg error (reg == 0x%02x, "
Harvey Harrison271ddbf2008-04-08 23:20:00 -030069 "val == 0x%02x, ret == %i)\n", __func__, reg, data, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 return (ret != 1) ? -EREMOTEIO : 0;
72}
73
74static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
75{
76 u8 b0[] = { 0x00, reg };
77 u8 b1[] = { 0 };
78 struct i2c_msg msg[] = {
79 {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
80 {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
81 };
82 int ret;
83
84 ret = i2c_transfer(state->i2c, msg, 2);
85
86 if (ret != 2)
Joe Perches11645cc2007-11-19 22:48:15 -030087 printk("ves1820: %s(): readreg error (reg == 0x%02x, "
Harvey Harrison271ddbf2008-04-08 23:20:00 -030088 "ret == %i)\n", __func__, reg, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
90 return b1[0];
91}
92
93static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion)
94{
95 reg0 |= state->reg0 & 0x62;
96
97 if (INVERSION_ON == inversion) {
98 if (!state->config->invert) reg0 |= 0x20;
99 else reg0 &= ~0x20;
100 } else if (INVERSION_OFF == inversion) {
101 if (!state->config->invert) reg0 &= ~0x20;
102 else reg0 |= 0x20;
103 }
104
105 ves1820_writereg(state, 0x00, reg0 & 0xfe);
106 ves1820_writereg(state, 0x00, reg0 | 0x01);
107
108 state->reg0 = reg0;
109
110 return 0;
111}
112
113static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
114{
115 s32 BDR;
116 s32 BDRI;
117 s16 SFIL = 0;
118 u16 NDEC = 0;
119 u32 ratio;
120 u32 fin;
121 u32 tmp;
122 u64 fptmp;
123 u64 fpxin;
124
125 if (symbolrate > state->config->xin / 2)
126 symbolrate = state->config->xin / 2;
127
128 if (symbolrate < 500000)
129 symbolrate = 500000;
130
131 if (symbolrate < state->config->xin / 16)
132 NDEC = 1;
133 if (symbolrate < state->config->xin / 32)
134 NDEC = 2;
135 if (symbolrate < state->config->xin / 64)
136 NDEC = 3;
137
138 /* yeuch! */
139 fpxin = state->config->xin * 10;
140 fptmp = fpxin; do_div(fptmp, 123);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800141 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 SFIL = 1;
143 fptmp = fpxin; do_div(fptmp, 160);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800144 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 SFIL = 0;
146 fptmp = fpxin; do_div(fptmp, 246);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800147 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 SFIL = 1;
149 fptmp = fpxin; do_div(fptmp, 320);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800150 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 SFIL = 0;
152 fptmp = fpxin; do_div(fptmp, 492);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800153 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 SFIL = 1;
155 fptmp = fpxin; do_div(fptmp, 640);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800156 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 SFIL = 0;
158 fptmp = fpxin; do_div(fptmp, 984);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800159 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 SFIL = 1;
161
162 fin = state->config->xin >> 4;
163 symbolrate <<= NDEC;
164 ratio = (symbolrate << 4) / fin;
165 tmp = ((symbolrate << 4) % fin) << 8;
166 ratio = (ratio << 8) + tmp / fin;
167 tmp = (tmp % fin) << 8;
168 ratio = (ratio << 8) + (tmp + fin / 2) / fin;
169
170 BDR = ratio;
171 BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
172
173 if (BDRI > 0xFF)
174 BDRI = 0xFF;
175
176 SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
177
178 NDEC = (NDEC << 6) | ves1820_inittab[0x03];
179
180 ves1820_writereg(state, 0x03, NDEC);
181 ves1820_writereg(state, 0x0a, BDR & 0xff);
182 ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
183 ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
184
185 ves1820_writereg(state, 0x0d, BDRI);
186 ves1820_writereg(state, 0x0e, SFIL);
187
188 return 0;
189}
190
191static int ves1820_init(struct dvb_frontend* fe)
192{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700193 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196 ves1820_writereg(state, 0, 0);
197
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700198 for (i = 0; i < sizeof(ves1820_inittab); i++)
199 ves1820_writereg(state, i, ves1820_inittab[i]);
200 if (state->config->selagc)
201 ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203 ves1820_writereg(state, 0x34, state->pwm);
204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 return 0;
206}
207
208static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
209{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700210 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
212 static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
213 static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
214 static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
215 static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
216 int real_qam = p->u.qam.modulation - QAM_16;
217
218 if (real_qam < 0 || real_qam > 4)
219 return -EINVAL;
220
Patrick Boettcherdea74862006-05-14 05:01:31 -0300221 if (fe->ops.tuner_ops.set_params) {
222 fe->ops.tuner_ops.set_params(fe, p);
223 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
Andrew de Quincey58b119e2006-04-18 17:47:10 -0300224 }
225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 ves1820_set_symbolrate(state, p->u.qam.symbol_rate);
227 ves1820_writereg(state, 0x34, state->pwm);
228
229 ves1820_writereg(state, 0x01, reg0x01[real_qam]);
230 ves1820_writereg(state, 0x05, reg0x05[real_qam]);
231 ves1820_writereg(state, 0x08, reg0x08[real_qam]);
232 ves1820_writereg(state, 0x09, reg0x09[real_qam]);
233
234 ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700235 ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 return 0;
237}
238
239static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status)
240{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700241 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 int sync;
243
244 *status = 0;
245 sync = ves1820_readreg(state, 0x11);
246
247 if (sync & 1)
248 *status |= FE_HAS_SIGNAL;
249
250 if (sync & 2)
251 *status |= FE_HAS_CARRIER;
252
253 if (sync & 2) /* XXX FIXME! */
254 *status |= FE_HAS_VITERBI;
255
256 if (sync & 4)
257 *status |= FE_HAS_SYNC;
258
259 if (sync & 8)
260 *status |= FE_HAS_LOCK;
261
262 return 0;
263}
264
265static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
266{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700267 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 u32 _ber = ves1820_readreg(state, 0x14) |
270 (ves1820_readreg(state, 0x15) << 8) |
271 ((ves1820_readreg(state, 0x16) & 0x0f) << 16);
272 *ber = 10 * _ber;
273
274 return 0;
275}
276
277static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
278{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700279 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281 u8 gain = ves1820_readreg(state, 0x17);
282 *strength = (gain << 8) | gain;
283
284 return 0;
285}
286
287static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
288{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700289 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 u8 quality = ~ves1820_readreg(state, 0x18);
292 *snr = (quality << 8) | quality;
293
294 return 0;
295}
296
297static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
298{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700299 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 *ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
302 if (*ucblocks == 0x7f)
303 *ucblocks = 0xffffffff;
304
305 /* reset uncorrected block counter */
306 ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
307 ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
308
309 return 0;
310}
311
312static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
313{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700314 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 int sync;
316 s8 afc = 0;
317
318 sync = ves1820_readreg(state, 0x11);
319 afc = ves1820_readreg(state, 0x19);
320 if (verbose) {
321 /* AFC only valid when carrier has been recovered */
322 printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
323 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
324 }
325
326 if (!state->config->invert) {
327 p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
328 } else {
329 p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
330 }
331
332 p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
333
334 p->u.qam.fec_inner = FEC_NONE;
335
336 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
337 if (sync & 2)
338 p->frequency -= ((s32) p->u.qam.symbol_rate * afc) >> 10;
339
340 return 0;
341}
342
343static int ves1820_sleep(struct dvb_frontend* fe)
344{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700345 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
347 ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
348 ves1820_writereg(state, 0x00, 0x80); /* standby */
349
350 return 0;
351}
352
353static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
354{
355
356 fesettings->min_delay_ms = 200;
357 fesettings->step_size = 0;
358 fesettings->max_drift = 0;
359 return 0;
360}
361
362static void ves1820_release(struct dvb_frontend* fe)
363{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700364 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 kfree(state);
366}
367
368static struct dvb_frontend_ops ves1820_ops;
369
370struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
371 struct i2c_adapter* i2c,
372 u8 pwm)
373{
374 struct ves1820_state* state = NULL;
375
376 /* allocate memory for the internal state */
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700377 state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 if (state == NULL)
379 goto error;
380
381 /* setup the state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 state->reg0 = ves1820_inittab[0];
383 state->config = config;
384 state->i2c = i2c;
385 state->pwm = pwm;
386
387 /* check if the demod is there */
388 if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
389 goto error;
390
391 if (verbose)
392 printk("ves1820: pwm=0x%02x\n", state->pwm);
393
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 /* create dvb_frontend */
Patrick Boettcherdea74862006-05-14 05:01:31 -0300395 memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
396 state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
397 state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 state->frontend.demodulator_priv = state;
Patrick Boettcherdea74862006-05-14 05:01:31 -0300399
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return &state->frontend;
401
402error:
403 kfree(state);
404 return NULL;
405}
406
407static struct dvb_frontend_ops ves1820_ops = {
408
409 .info = {
410 .name = "VLSI VES1820 DVB-C",
411 .type = FE_QAM,
412 .frequency_stepsize = 62500,
Hartmut Birra18255b2007-08-09 00:01:51 -0300413 .frequency_min = 47000000,
414 .frequency_max = 862000000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 .caps = FE_CAN_QAM_16 |
416 FE_CAN_QAM_32 |
417 FE_CAN_QAM_64 |
418 FE_CAN_QAM_128 |
419 FE_CAN_QAM_256 |
420 FE_CAN_FEC_AUTO
421 },
422
423 .release = ves1820_release,
424
425 .init = ves1820_init,
426 .sleep = ves1820_sleep,
427
428 .set_frontend = ves1820_set_parameters,
429 .get_frontend = ves1820_get_frontend,
430 .get_tune_settings = ves1820_get_tune_settings,
431
432 .read_status = ves1820_read_status,
433 .read_ber = ves1820_read_ber,
434 .read_signal_strength = ves1820_read_signal_strength,
435 .read_snr = ves1820_read_snr,
436 .read_ucblocks = ves1820_read_ucblocks,
437};
438
439module_param(verbose, int, 0644);
440MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
441
442MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
443MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
444MODULE_LICENSE("GPL");
445
446EXPORT_SYMBOL(ves1820_attach);