blob: aacfdda3e005ab7c0b763a71eeded7812346aaac [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
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -030093static int ves1820_setup_reg0(struct ves1820_state *state,
94 u8 reg0, enum fe_spectral_inversion inversion)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
96 reg0 |= state->reg0 & 0x62;
97
98 if (INVERSION_ON == inversion) {
99 if (!state->config->invert) reg0 |= 0x20;
100 else reg0 &= ~0x20;
101 } else if (INVERSION_OFF == inversion) {
102 if (!state->config->invert) reg0 &= ~0x20;
103 else reg0 |= 0x20;
104 }
105
106 ves1820_writereg(state, 0x00, reg0 & 0xfe);
107 ves1820_writereg(state, 0x00, reg0 | 0x01);
108
109 state->reg0 = reg0;
110
111 return 0;
112}
113
114static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
115{
116 s32 BDR;
117 s32 BDRI;
118 s16 SFIL = 0;
119 u16 NDEC = 0;
120 u32 ratio;
121 u32 fin;
122 u32 tmp;
123 u64 fptmp;
124 u64 fpxin;
125
126 if (symbolrate > state->config->xin / 2)
127 symbolrate = state->config->xin / 2;
128
129 if (symbolrate < 500000)
130 symbolrate = 500000;
131
132 if (symbolrate < state->config->xin / 16)
133 NDEC = 1;
134 if (symbolrate < state->config->xin / 32)
135 NDEC = 2;
136 if (symbolrate < state->config->xin / 64)
137 NDEC = 3;
138
139 /* yeuch! */
140 fpxin = state->config->xin * 10;
141 fptmp = fpxin; do_div(fptmp, 123);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800142 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 SFIL = 1;
144 fptmp = fpxin; do_div(fptmp, 160);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800145 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 SFIL = 0;
147 fptmp = fpxin; do_div(fptmp, 246);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800148 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 SFIL = 1;
150 fptmp = fpxin; do_div(fptmp, 320);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800151 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 SFIL = 0;
153 fptmp = fpxin; do_div(fptmp, 492);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800154 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 SFIL = 1;
156 fptmp = fpxin; do_div(fptmp, 640);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800157 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 SFIL = 0;
159 fptmp = fpxin; do_div(fptmp, 984);
Denis Vlasenko48063a72005-12-01 00:51:55 -0800160 if (symbolrate < fptmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 SFIL = 1;
162
163 fin = state->config->xin >> 4;
164 symbolrate <<= NDEC;
165 ratio = (symbolrate << 4) / fin;
166 tmp = ((symbolrate << 4) % fin) << 8;
167 ratio = (ratio << 8) + tmp / fin;
168 tmp = (tmp % fin) << 8;
Julia Lawall75b697f2009-08-01 16:48:41 -0300169 ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
171 BDR = ratio;
172 BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
173
174 if (BDRI > 0xFF)
175 BDRI = 0xFF;
176
177 SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
178
179 NDEC = (NDEC << 6) | ves1820_inittab[0x03];
180
181 ves1820_writereg(state, 0x03, NDEC);
182 ves1820_writereg(state, 0x0a, BDR & 0xff);
183 ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
184 ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
185
186 ves1820_writereg(state, 0x0d, BDRI);
187 ves1820_writereg(state, 0x0e, SFIL);
188
189 return 0;
190}
191
192static int ves1820_init(struct dvb_frontend* fe)
193{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700194 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197 ves1820_writereg(state, 0, 0);
198
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700199 for (i = 0; i < sizeof(ves1820_inittab); i++)
200 ves1820_writereg(state, i, ves1820_inittab[i]);
201 if (state->config->selagc)
202 ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204 ves1820_writereg(state, 0x34, state->pwm);
205
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 return 0;
207}
208
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300209static int ves1820_set_parameters(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300211 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700212 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
214 static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
215 static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
216 static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
217 static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300218 int real_qam = p->modulation - QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
220 if (real_qam < 0 || real_qam > 4)
221 return -EINVAL;
222
Patrick Boettcherdea74862006-05-14 05:01:31 -0300223 if (fe->ops.tuner_ops.set_params) {
Mauro Carvalho Chehab14d24d12011-12-24 12:24:33 -0300224 fe->ops.tuner_ops.set_params(fe);
Patrick Boettcherdea74862006-05-14 05:01:31 -0300225 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
Andrew de Quincey58b119e2006-04-18 17:47:10 -0300226 }
227
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300228 ves1820_set_symbolrate(state, p->symbol_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 ves1820_writereg(state, 0x34, state->pwm);
230
231 ves1820_writereg(state, 0x01, reg0x01[real_qam]);
232 ves1820_writereg(state, 0x05, reg0x05[real_qam]);
233 ves1820_writereg(state, 0x08, reg0x08[real_qam]);
234 ves1820_writereg(state, 0x09, reg0x09[real_qam]);
235
236 ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
Johannes Stezenbach6816a4c2005-09-09 13:02:34 -0700237 ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 return 0;
239}
240
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -0300241static int ves1820_read_status(struct dvb_frontend *fe,
242 enum fe_status *status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700244 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 int sync;
246
247 *status = 0;
248 sync = ves1820_readreg(state, 0x11);
249
250 if (sync & 1)
251 *status |= FE_HAS_SIGNAL;
252
253 if (sync & 2)
254 *status |= FE_HAS_CARRIER;
255
256 if (sync & 2) /* XXX FIXME! */
257 *status |= FE_HAS_VITERBI;
258
259 if (sync & 4)
260 *status |= FE_HAS_SYNC;
261
262 if (sync & 8)
263 *status |= FE_HAS_LOCK;
264
265 return 0;
266}
267
268static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
269{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700270 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 u32 _ber = ves1820_readreg(state, 0x14) |
273 (ves1820_readreg(state, 0x15) << 8) |
274 ((ves1820_readreg(state, 0x16) & 0x0f) << 16);
275 *ber = 10 * _ber;
276
277 return 0;
278}
279
280static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
281{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700282 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284 u8 gain = ves1820_readreg(state, 0x17);
285 *strength = (gain << 8) | gain;
286
287 return 0;
288}
289
290static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
291{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700292 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 u8 quality = ~ves1820_readreg(state, 0x18);
295 *snr = (quality << 8) | quality;
296
297 return 0;
298}
299
300static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
301{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700302 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
304 *ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
305 if (*ucblocks == 0x7f)
306 *ucblocks = 0xffffffff;
307
308 /* reset uncorrected block counter */
309 ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
310 ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
311
312 return 0;
313}
314
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300315static int ves1820_get_frontend(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316{
Mauro Carvalho Chehab7c61d802011-12-30 11:30:21 -0300317 struct dtv_frontend_properties *p = &fe->dtv_property_cache;
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700318 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 int sync;
320 s8 afc = 0;
321
322 sync = ves1820_readreg(state, 0x11);
323 afc = ves1820_readreg(state, 0x19);
324 if (verbose) {
325 /* AFC only valid when carrier has been recovered */
326 printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300327 "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }
329
330 if (!state->config->invert) {
331 p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
332 } else {
333 p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
334 }
335
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300336 p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300338 p->fec_inner = FEC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 p->frequency = ((p->frequency + 31250) / 62500) * 62500;
341 if (sync & 2)
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300342 p->frequency -= ((s32) p->symbol_rate * afc) >> 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344 return 0;
345}
346
347static int ves1820_sleep(struct dvb_frontend* fe)
348{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700349 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
352 ves1820_writereg(state, 0x00, 0x80); /* standby */
353
354 return 0;
355}
356
357static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
358{
359
360 fesettings->min_delay_ms = 200;
361 fesettings->step_size = 0;
362 fesettings->max_drift = 0;
363 return 0;
364}
365
366static void ves1820_release(struct dvb_frontend* fe)
367{
Johannes Stezenbachb8742702005-05-16 21:54:31 -0700368 struct ves1820_state* state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 kfree(state);
370}
371
372static struct dvb_frontend_ops ves1820_ops;
373
374struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
375 struct i2c_adapter* i2c,
376 u8 pwm)
377{
378 struct ves1820_state* state = NULL;
379
380 /* allocate memory for the internal state */
Matthias Schwarzott084e24a2009-08-10 22:51:01 -0300381 state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 if (state == NULL)
383 goto error;
384
385 /* setup the state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 state->reg0 = ves1820_inittab[0];
387 state->config = config;
388 state->i2c = i2c;
389 state->pwm = pwm;
390
391 /* check if the demod is there */
392 if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
393 goto error;
394
395 if (verbose)
396 printk("ves1820: pwm=0x%02x\n", state->pwm);
397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 /* create dvb_frontend */
Patrick Boettcherdea74862006-05-14 05:01:31 -0300399 memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
400 state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
401 state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 state->frontend.demodulator_priv = state;
Patrick Boettcherdea74862006-05-14 05:01:31 -0300403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 return &state->frontend;
405
406error:
407 kfree(state);
408 return NULL;
409}
410
411static struct dvb_frontend_ops ves1820_ops = {
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300412 .delsys = { SYS_DVBC_ANNEX_A },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 .info = {
414 .name = "VLSI VES1820 DVB-C",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 .frequency_stepsize = 62500,
Hartmut Birra18255b2007-08-09 00:01:51 -0300416 .frequency_min = 47000000,
417 .frequency_max = 862000000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 .caps = FE_CAN_QAM_16 |
419 FE_CAN_QAM_32 |
420 FE_CAN_QAM_64 |
421 FE_CAN_QAM_128 |
422 FE_CAN_QAM_256 |
423 FE_CAN_FEC_AUTO
424 },
425
426 .release = ves1820_release,
427
428 .init = ves1820_init,
429 .sleep = ves1820_sleep,
430
Mauro Carvalho Chehabf6c69962011-12-26 15:09:31 -0300431 .set_frontend = ves1820_set_parameters,
432 .get_frontend = ves1820_get_frontend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 .get_tune_settings = ves1820_get_tune_settings,
434
435 .read_status = ves1820_read_status,
436 .read_ber = ves1820_read_ber,
437 .read_signal_strength = ves1820_read_signal_strength,
438 .read_snr = ves1820_read_snr,
439 .read_ucblocks = ves1820_read_ucblocks,
440};
441
442module_param(verbose, int, 0644);
443MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
444
445MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
446MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
447MODULE_LICENSE("GPL");
448
449EXPORT_SYMBOL(ves1820_attach);