blob: e85a823665ff8485a79b19984c5b001f3ef5c561 [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 Chehab7c61d802011-12-30 11:30:21 -0300313static int ves1820_get_frontend(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300315 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700316 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 int sync;
318 s8 afc = 0;
319
320 sync = ves1820_readreg(state, 0x11);
321 afc = ves1820_readreg(state, 0x19);
322 if (verbose) {
323 /* AFC only valid when carrier has been recovered */
324 printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300325 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
328 if (!state->config->invert) {
329 p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
330 } else {
331 p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
332 }
333
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300334 p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300336 p->fec_inner = FEC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
339 if (sync & 2)
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300340 p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342 return 0;
343}
344
345static int ves1820_sleep(struct dvb_frontend* fe)
346{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700347 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
350 ves1820_writereg(state, 0x00, 0x80); /* standby */
351
352 return 0;
353}
354
355static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
356{
357
358 fesettings->min_delay_ms = 200;
359 fesettings->step_size = 0;
360 fesettings->max_drift = 0;
361 return 0;
362}
363
364static void ves1820_release(struct dvb_frontend* fe)
365{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700366 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 kfree(state);
368}
369
370static struct dvb_frontend_ops ves1820_ops;
371
372struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
373 struct i2c_adapter* i2c,
374 u8 pwm)
375{
376 struct ves1820_state* state = NULL;
377
378 /* allocate memory for the internal state */
Matthias Schwarzott084e24a2009-08-10 22:51:01 -0300379 state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 if (state == NULL)
381 goto error;
382
383 /* setup the state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 state->reg0 = ves1820_inittab[0];
385 state->config = config;
386 state->i2c = i2c;
387 state->pwm = pwm;
388
389 /* check if the demod is there */
390 if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
391 goto error;
392
393 if (verbose)
394 printk("ves1820: pwm=0x%02x\n", state->pwm);
395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 /* create dvb_frontend */
Patrick Boettcherdea74862006-05-14 05:01:31 -0300397 memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
398 state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
399 state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 state->frontend.demodulator_priv = state;
Patrick Boettcherdea74862006-05-14 05:01:31 -0300401
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 return &state->frontend;
403
404error:
405 kfree(state);
406 return NULL;
407}
408
409static struct dvb_frontend_ops ves1820_ops = {
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300410 .delsys = { SYS_DVBC_ANNEX_A },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 .info = {
412 .name = "VLSI VES1820 DVB-C",
413 .type = FE_QAM,
414 .frequency_stepsize = 62500,
Hartmut Birra18255b2007-08-09 00:01:51 -0300415 .frequency_min = 47000000,
416 .frequency_max = 862000000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 .caps = FE_CAN_QAM_16 |
418 FE_CAN_QAM_32 |
419 FE_CAN_QAM_64 |
420 FE_CAN_QAM_128 |
421 FE_CAN_QAM_256 |
422 FE_CAN_FEC_AUTO
423 },
424
425 .release = ves1820_release,
426
427 .init = ves1820_init,
428 .sleep = ves1820_sleep,
429
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300430 .set_frontend = ves1820_set_parameters,
431 .get_frontend = ves1820_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 .get_tune_settings = ves1820_get_tune_settings,
433
434 .read_status = ves1820_read_status,
435 .read_ber = ves1820_read_ber,
436 .read_signal_strength = ves1820_read_signal_strength,
437 .read_snr = ves1820_read_snr,
438 .read_ucblocks = ves1820_read_ucblocks,
439};
440
441module_param(verbose, int, 0644);
442MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
443
444MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
445MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
446MODULE_LICENSE("GPL");
447
448EXPORT_SYMBOL(ves1820_attach);