blob: ecc685b3ab3c2c4e5c9f931698ab52e46c51ed46 [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;
Julia Lawall75b697f2009-08-01 16:48:41 -0300168 ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
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
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300208static int ves1820_set_parameters(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300210 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700211 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
213 static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
214 static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
215 static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
216 static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300217 int real_qam = p->modulation - QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219 if (real_qam < 0 || real_qam > 4)
220 return -EINVAL;
221
Patrick Boettcherdea74862006-05-14 05:01:31 -0300222 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300223 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherdea74862006-05-14 05:01:31 -0300224 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
Andrew de Quincey58b119e2006-04-18 17:47:10 -0300225 }
226
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300227 ves1820_set_symbolrate(state, p->symbol_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 ves1820_writereg(state, 0x34, state->pwm);
229
230 ves1820_writereg(state, 0x01, reg0x01[real_qam]);
231 ves1820_writereg(state, 0x05, reg0x05[real_qam]);
232 ves1820_writereg(state, 0x08, reg0x08[real_qam]);
233 ves1820_writereg(state, 0x09, reg0x09[real_qam]);
234
235 ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700236 ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return 0;
238}
239
240static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status)
241{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700242 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 int sync;
244
245 *status = 0;
246 sync = ves1820_readreg(state, 0x11);
247
248 if (sync & 1)
249 *status |= FE_HAS_SIGNAL;
250
251 if (sync & 2)
252 *status |= FE_HAS_CARRIER;
253
254 if (sync & 2) /* XXX FIXME! */
255 *status |= FE_HAS_VITERBI;
256
257 if (sync & 4)
258 *status |= FE_HAS_SYNC;
259
260 if (sync & 8)
261 *status |= FE_HAS_LOCK;
262
263 return 0;
264}
265
266static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
267{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700268 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
270 u32 _ber = ves1820_readreg(state, 0x14) |
271 (ves1820_readreg(state, 0x15) << 8) |
272 ((ves1820_readreg(state, 0x16) & 0x0f) << 16);
273 *ber = 10 * _ber;
274
275 return 0;
276}
277
278static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
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 gain = ves1820_readreg(state, 0x17);
283 *strength = (gain << 8) | gain;
284
285 return 0;
286}
287
288static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
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 u8 quality = ~ves1820_readreg(state, 0x18);
293 *snr = (quality << 8) | quality;
294
295 return 0;
296}
297
298static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
299{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700300 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 *ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
303 if (*ucblocks == 0x7f)
304 *ucblocks = 0xffffffff;
305
306 /* reset uncorrected block counter */
307 ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
308 ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
309
310 return 0;
311}
312
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300313static int ves1820_get_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700315 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 int sync;
317 s8 afc = 0;
318
319 sync = ves1820_readreg(state, 0x11);
320 afc = ves1820_readreg(state, 0x19);
321 if (verbose) {
322 /* AFC only valid when carrier has been recovered */
323 printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300324 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
326
327 if (!state->config->invert) {
328 p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
329 } else {
330 p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
331 }
332
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300333 p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300335 p->fec_inner = FEC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
338 if (sync & 2)
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300339 p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341 return 0;
342}
343
344static int ves1820_sleep(struct dvb_frontend* fe)
345{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700346 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348 ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
349 ves1820_writereg(state, 0x00, 0x80); /* standby */
350
351 return 0;
352}
353
354static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
355{
356
357 fesettings->min_delay_ms = 200;
358 fesettings->step_size = 0;
359 fesettings->max_drift = 0;
360 return 0;
361}
362
363static void ves1820_release(struct dvb_frontend* fe)
364{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700365 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 kfree(state);
367}
368
369static struct dvb_frontend_ops ves1820_ops;
370
371struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
372 struct i2c_adapter* i2c,
373 u8 pwm)
374{
375 struct ves1820_state* state = NULL;
376
377 /* allocate memory for the internal state */
Matthias Schwarzott084e24a2009-08-10 22:51:01 -0300378 state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 if (state == NULL)
380 goto error;
381
382 /* setup the state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 state->reg0 = ves1820_inittab[0];
384 state->config = config;
385 state->i2c = i2c;
386 state->pwm = pwm;
387
388 /* check if the demod is there */
389 if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
390 goto error;
391
392 if (verbose)
393 printk("ves1820: pwm=0x%02x\n", state->pwm);
394
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 /* create dvb_frontend */
Patrick Boettcherdea74862006-05-14 05:01:31 -0300396 memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
397 state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
398 state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 state->frontend.demodulator_priv = state;
Patrick Boettcherdea74862006-05-14 05:01:31 -0300400
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 return &state->frontend;
402
403error:
404 kfree(state);
405 return NULL;
406}
407
408static struct dvb_frontend_ops ves1820_ops = {
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300409 .delsys = { SYS_DVBC_ANNEX_A },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 .info = {
411 .name = "VLSI VES1820 DVB-C",
412 .type = FE_QAM,
413 .frequency_stepsize = 62500,
Hartmut Birra18255b2007-08-09 00:01:51 -0300414 .frequency_min = 47000000,
415 .frequency_max = 862000000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 .caps = FE_CAN_QAM_16 |
417 FE_CAN_QAM_32 |
418 FE_CAN_QAM_64 |
419 FE_CAN_QAM_128 |
420 FE_CAN_QAM_256 |
421 FE_CAN_FEC_AUTO
422 },
423
424 .release = ves1820_release,
425
426 .init = ves1820_init,
427 .sleep = ves1820_sleep,
428
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300429 .set_frontend = ves1820_set_parameters,
430 .get_frontend = ves1820_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 .get_tune_settings = ves1820_get_tune_settings,
432
433 .read_status = ves1820_read_status,
434 .read_ber = ves1820_read_ber,
435 .read_signal_strength = ves1820_read_signal_strength,
436 .read_snr = ves1820_read_snr,
437 .read_ucblocks = ves1820_read_ucblocks,
438};
439
440module_param(verbose, int, 0644);
441MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
442
443MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
444MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
445MODULE_LICENSE("GPL");
446
447EXPORT_SYMBOL(ves1820_attach);