blob: 3139558148bacb1f92a972dbaff2baf27ef4f18a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 Conexant 22702 DVB OFDM demodulator driver
3
4 based on:
Mauro Carvalho Chehab9101e622005-12-12 00:37:24 -08005 Alps TDMB7 DVB OFDM demodulator driver
Linus Torvalds1da177e2005-04-16 15:20:36 -07006
7 Copyright (C) 2001-2002 Convergence Integrated Media GmbH
8 Holger Waechtler <holger@convergence.de>
9
Steven Toth6d897612008-09-03 17:12:12 -030010 Copyright (C) 2004 Steven Toth <stoth@linuxtv.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26*/
27
28#include <linux/kernel.h>
29#include <linux/init.h>
30#include <linux/module.h>
31#include <linux/string.h>
32#include <linux/slab.h>
33#include <linux/delay.h>
34#include "dvb_frontend.h"
35#include "cx22702.h"
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037struct cx22702_state {
38
Steven Toth4e3599a2008-10-16 20:23:45 -030039 struct i2c_adapter *i2c;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 /* configuration settings */
Steven Toth4e3599a2008-10-16 20:23:45 -030042 const struct cx22702_config *config;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44 struct dvb_frontend frontend;
45
46 /* previous uncorrected block counter */
47 u8 prevUCBlocks;
48};
49
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030050static int debug;
Steven Toth4e3599a2008-10-16 20:23:45 -030051module_param(debug, int, 0644);
52MODULE_PARM_DESC(debug, "Enable verbose debug messages");
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define dprintk if (debug) printk
55
56/* Register values to initialise the demod */
Jean Delvarebdc6fad2010-09-10 10:35:12 -030057static const u8 init_tab[] = {
Lucas De Marchi25985ed2011-03-30 22:57:33 -030058 0x00, 0x00, /* Stop acquisition */
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 0x0B, 0x06,
60 0x09, 0x01,
61 0x0D, 0x41,
62 0x16, 0x32,
63 0x20, 0x0A,
64 0x21, 0x17,
65 0x24, 0x3e,
66 0x26, 0xff,
67 0x27, 0x10,
68 0x28, 0x00,
69 0x29, 0x00,
70 0x2a, 0x10,
71 0x2b, 0x00,
72 0x2c, 0x10,
73 0x2d, 0x00,
74 0x48, 0xd4,
75 0x49, 0x56,
76 0x6b, 0x1e,
77 0xc8, 0x02,
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 0xf9, 0x00,
79 0xfa, 0x00,
80 0xfb, 0x00,
81 0xfc, 0x00,
82 0xfd, 0x00,
83};
84
Steven Toth4e3599a2008-10-16 20:23:45 -030085static int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 int ret;
Steven Toth4e3599a2008-10-16 20:23:45 -030088 u8 buf[] = { reg, data };
89 struct i2c_msg msg = {
90 .addr = state->config->demod_address, .flags = 0,
91 .buf = buf, .len = 2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93 ret = i2c_transfer(state->i2c, &msg, 1);
94
Jean Delvare24764102010-09-10 14:03:07 -030095 if (unlikely(ret != 1)) {
Steven Toth4e3599a2008-10-16 20:23:45 -030096 printk(KERN_ERR
97 "%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
Harvey Harrison271ddbf2008-04-08 23:20:00 -030098 __func__, reg, data, ret);
Jean Delvare24764102010-09-10 14:03:07 -030099 return -1;
100 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Jean Delvare24764102010-09-10 14:03:07 -0300102 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
Steven Toth4e3599a2008-10-16 20:23:45 -0300105static u8 cx22702_readreg(struct cx22702_state *state, u8 reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106{
107 int ret;
Jean Delvare24764102010-09-10 14:03:07 -0300108 u8 data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
Steven Toth4e3599a2008-10-16 20:23:45 -0300110 struct i2c_msg msg[] = {
111 { .addr = state->config->demod_address, .flags = 0,
Jean Delvare24764102010-09-10 14:03:07 -0300112 .buf = &reg, .len = 1 },
Steven Toth4e3599a2008-10-16 20:23:45 -0300113 { .addr = state->config->demod_address, .flags = I2C_M_RD,
Jean Delvare24764102010-09-10 14:03:07 -0300114 .buf = &data, .len = 1 } };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
116 ret = i2c_transfer(state->i2c, msg, 2);
117
Jean Delvare24764102010-09-10 14:03:07 -0300118 if (unlikely(ret != 2)) {
119 printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n",
120 __func__, reg, ret);
121 return 0;
122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Jean Delvare24764102010-09-10 14:03:07 -0300124 return data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126
Steven Toth4e3599a2008-10-16 20:23:45 -0300127static int cx22702_set_inversion(struct cx22702_state *state, int inversion)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
129 u8 val;
130
Jean Delvare27f84ac2010-09-10 10:33:42 -0300131 val = cx22702_readreg(state, 0x0C);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 switch (inversion) {
Steven Toth4e3599a2008-10-16 20:23:45 -0300133 case INVERSION_AUTO:
134 return -EOPNOTSUPP;
135 case INVERSION_ON:
Jean Delvare27f84ac2010-09-10 10:33:42 -0300136 val |= 0x01;
137 break;
Steven Toth4e3599a2008-10-16 20:23:45 -0300138 case INVERSION_OFF:
Jean Delvare27f84ac2010-09-10 10:33:42 -0300139 val &= 0xfe;
140 break;
Steven Toth4e3599a2008-10-16 20:23:45 -0300141 default:
142 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
Jean Delvare27f84ac2010-09-10 10:33:42 -0300144 return cx22702_writereg(state, 0x0C, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
146
147/* Retrieve the demod settings */
Steven Toth4e3599a2008-10-16 20:23:45 -0300148static int cx22702_get_tps(struct cx22702_state *state,
149 struct dvb_ofdm_parameters *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 u8 val;
152
153 /* Make sure the TPS regs are valid */
154 if (!(cx22702_readreg(state, 0x0A) & 0x20))
155 return -EAGAIN;
156
Steven Toth4e3599a2008-10-16 20:23:45 -0300157 val = cx22702_readreg(state, 0x01);
158 switch ((val & 0x18) >> 3) {
159 case 0:
160 p->constellation = QPSK;
161 break;
162 case 1:
163 p->constellation = QAM_16;
164 break;
165 case 2:
166 p->constellation = QAM_64;
167 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 }
Steven Toth4e3599a2008-10-16 20:23:45 -0300169 switch (val & 0x07) {
170 case 0:
171 p->hierarchy_information = HIERARCHY_NONE;
172 break;
173 case 1:
174 p->hierarchy_information = HIERARCHY_1;
175 break;
176 case 2:
177 p->hierarchy_information = HIERARCHY_2;
178 break;
179 case 3:
180 p->hierarchy_information = HIERARCHY_4;
181 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
183
184
Steven Toth4e3599a2008-10-16 20:23:45 -0300185 val = cx22702_readreg(state, 0x02);
186 switch ((val & 0x38) >> 3) {
187 case 0:
188 p->code_rate_HP = FEC_1_2;
189 break;
190 case 1:
191 p->code_rate_HP = FEC_2_3;
192 break;
193 case 2:
194 p->code_rate_HP = FEC_3_4;
195 break;
196 case 3:
197 p->code_rate_HP = FEC_5_6;
198 break;
199 case 4:
200 p->code_rate_HP = FEC_7_8;
201 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 }
Steven Toth4e3599a2008-10-16 20:23:45 -0300203 switch (val & 0x07) {
204 case 0:
205 p->code_rate_LP = FEC_1_2;
206 break;
207 case 1:
208 p->code_rate_LP = FEC_2_3;
209 break;
210 case 2:
211 p->code_rate_LP = FEC_3_4;
212 break;
213 case 3:
214 p->code_rate_LP = FEC_5_6;
215 break;
216 case 4:
217 p->code_rate_LP = FEC_7_8;
218 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 }
220
Steven Toth4e3599a2008-10-16 20:23:45 -0300221 val = cx22702_readreg(state, 0x03);
222 switch ((val & 0x0c) >> 2) {
223 case 0:
224 p->guard_interval = GUARD_INTERVAL_1_32;
225 break;
226 case 1:
227 p->guard_interval = GUARD_INTERVAL_1_16;
228 break;
229 case 2:
230 p->guard_interval = GUARD_INTERVAL_1_8;
231 break;
232 case 3:
233 p->guard_interval = GUARD_INTERVAL_1_4;
234 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 }
Steven Toth4e3599a2008-10-16 20:23:45 -0300236 switch (val & 0x03) {
237 case 0:
238 p->transmission_mode = TRANSMISSION_MODE_2K;
239 break;
240 case 1:
241 p->transmission_mode = TRANSMISSION_MODE_8K;
242 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244
245 return 0;
246}
247
Steven Toth4e3599a2008-10-16 20:23:45 -0300248static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
Steven Toth611900c2006-01-09 15:25:12 -0200249{
Steven Toth4e3599a2008-10-16 20:23:45 -0300250 struct cx22702_state *state = fe->demodulator_priv;
Jean Delvare27f84ac2010-09-10 10:33:42 -0300251 u8 val;
252
Steven Toth4e3599a2008-10-16 20:23:45 -0300253 dprintk("%s(%d)\n", __func__, enable);
Jean Delvare27f84ac2010-09-10 10:33:42 -0300254 val = cx22702_readreg(state, 0x0D);
Steven Toth611900c2006-01-09 15:25:12 -0200255 if (enable)
Jean Delvare27f84ac2010-09-10 10:33:42 -0300256 val &= 0xfe;
Steven Toth611900c2006-01-09 15:25:12 -0200257 else
Jean Delvare27f84ac2010-09-10 10:33:42 -0300258 val |= 0x01;
259 return cx22702_writereg(state, 0x0D, val);
Steven Toth611900c2006-01-09 15:25:12 -0200260}
261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
Steven Toth4e3599a2008-10-16 20:23:45 -0300263static int cx22702_set_tps(struct dvb_frontend *fe,
264 struct dvb_frontend_parameters *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 u8 val;
Steven Toth4e3599a2008-10-16 20:23:45 -0300267 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
Patrick Boettcherdea74862006-05-14 05:01:31 -0300269 if (fe->ops.tuner_ops.set_params) {
270 fe->ops.tuner_ops.set_params(fe, p);
Steven Toth4e3599a2008-10-16 20:23:45 -0300271 if (fe->ops.i2c_gate_ctrl)
272 fe->ops.i2c_gate_ctrl(fe, 0);
Gerd Knorr9990d742005-05-01 08:59:20 -0700273 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275 /* set inversion */
Steven Toth4e3599a2008-10-16 20:23:45 -0300276 cx22702_set_inversion(state, p->inversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278 /* set bandwidth */
Jean Delvare27f84ac2010-09-10 10:33:42 -0300279 val = cx22702_readreg(state, 0x0C) & 0xcf;
Steven Toth4e3599a2008-10-16 20:23:45 -0300280 switch (p->u.ofdm.bandwidth) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 case BANDWIDTH_6_MHZ:
Jean Delvare27f84ac2010-09-10 10:33:42 -0300282 val |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 break;
284 case BANDWIDTH_7_MHZ:
Jean Delvare27f84ac2010-09-10 10:33:42 -0300285 val |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 break;
287 case BANDWIDTH_8_MHZ:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 break;
289 default:
Steven Toth4e3599a2008-10-16 20:23:45 -0300290 dprintk("%s: invalid bandwidth\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 return -EINVAL;
292 }
Jean Delvare27f84ac2010-09-10 10:33:42 -0300293 cx22702_writereg(state, 0x0C, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Steven Toth4e3599a2008-10-16 20:23:45 -0300295 p->u.ofdm.code_rate_LP = FEC_AUTO; /* temp hack as manual not working */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 /* use auto configuration? */
Steven Toth4e3599a2008-10-16 20:23:45 -0300298 if ((p->u.ofdm.hierarchy_information == HIERARCHY_AUTO) ||
299 (p->u.ofdm.constellation == QAM_AUTO) ||
300 (p->u.ofdm.code_rate_HP == FEC_AUTO) ||
301 (p->u.ofdm.code_rate_LP == FEC_AUTO) ||
302 (p->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO) ||
303 (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 /* TPS Source - use hardware driven values */
306 cx22702_writereg(state, 0x06, 0x10);
307 cx22702_writereg(state, 0x07, 0x9);
308 cx22702_writereg(state, 0x08, 0xC1);
Steven Toth4e3599a2008-10-16 20:23:45 -0300309 cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B)
310 & 0xfc);
311 cx22702_writereg(state, 0x0C,
312 (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40);
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300313 cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */
Steven Toth4e3599a2008-10-16 20:23:45 -0300314 dprintk("%s: Autodetecting\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 return 0;
316 }
317
318 /* manually programmed values */
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300319 switch (p->u.ofdm.constellation) { /* mask 0x18 */
Steven Toth4e3599a2008-10-16 20:23:45 -0300320 case QPSK:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300321 val = 0x00;
Steven Toth4e3599a2008-10-16 20:23:45 -0300322 break;
323 case QAM_16:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300324 val = 0x08;
Steven Toth4e3599a2008-10-16 20:23:45 -0300325 break;
326 case QAM_64:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300327 val = 0x10;
Steven Toth4e3599a2008-10-16 20:23:45 -0300328 break;
329 default:
330 dprintk("%s: invalid constellation\n", __func__);
331 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300333 switch (p->u.ofdm.hierarchy_information) { /* mask 0x07 */
Steven Toth4e3599a2008-10-16 20:23:45 -0300334 case HIERARCHY_NONE:
Steven Toth4e3599a2008-10-16 20:23:45 -0300335 break;
336 case HIERARCHY_1:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300337 val |= 0x01;
Steven Toth4e3599a2008-10-16 20:23:45 -0300338 break;
339 case HIERARCHY_2:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300340 val |= 0x02;
Steven Toth4e3599a2008-10-16 20:23:45 -0300341 break;
342 case HIERARCHY_4:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300343 val |= 0x03;
Steven Toth4e3599a2008-10-16 20:23:45 -0300344 break;
345 default:
346 dprintk("%s: invalid hierarchy\n", __func__);
347 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 }
Steven Toth4e3599a2008-10-16 20:23:45 -0300349 cx22702_writereg(state, 0x06, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300351 switch (p->u.ofdm.code_rate_HP) { /* mask 0x38 */
Steven Toth4e3599a2008-10-16 20:23:45 -0300352 case FEC_NONE:
353 case FEC_1_2:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300354 val = 0x00;
Steven Toth4e3599a2008-10-16 20:23:45 -0300355 break;
356 case FEC_2_3:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300357 val = 0x08;
Steven Toth4e3599a2008-10-16 20:23:45 -0300358 break;
359 case FEC_3_4:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300360 val = 0x10;
Steven Toth4e3599a2008-10-16 20:23:45 -0300361 break;
362 case FEC_5_6:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300363 val = 0x18;
Steven Toth4e3599a2008-10-16 20:23:45 -0300364 break;
365 case FEC_7_8:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300366 val = 0x20;
Steven Toth4e3599a2008-10-16 20:23:45 -0300367 break;
368 default:
369 dprintk("%s: invalid code_rate_HP\n", __func__);
370 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 }
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300372 switch (p->u.ofdm.code_rate_LP) { /* mask 0x07 */
Steven Toth4e3599a2008-10-16 20:23:45 -0300373 case FEC_NONE:
374 case FEC_1_2:
Steven Toth4e3599a2008-10-16 20:23:45 -0300375 break;
376 case FEC_2_3:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300377 val |= 0x01;
Steven Toth4e3599a2008-10-16 20:23:45 -0300378 break;
379 case FEC_3_4:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300380 val |= 0x02;
Steven Toth4e3599a2008-10-16 20:23:45 -0300381 break;
382 case FEC_5_6:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300383 val |= 0x03;
Steven Toth4e3599a2008-10-16 20:23:45 -0300384 break;
385 case FEC_7_8:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300386 val |= 0x04;
Steven Toth4e3599a2008-10-16 20:23:45 -0300387 break;
388 default:
389 dprintk("%s: invalid code_rate_LP\n", __func__);
390 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 }
Steven Toth4e3599a2008-10-16 20:23:45 -0300392 cx22702_writereg(state, 0x07, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300394 switch (p->u.ofdm.guard_interval) { /* mask 0x0c */
Steven Toth4e3599a2008-10-16 20:23:45 -0300395 case GUARD_INTERVAL_1_32:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300396 val = 0x00;
Steven Toth4e3599a2008-10-16 20:23:45 -0300397 break;
398 case GUARD_INTERVAL_1_16:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300399 val = 0x04;
Steven Toth4e3599a2008-10-16 20:23:45 -0300400 break;
401 case GUARD_INTERVAL_1_8:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300402 val = 0x08;
Steven Toth4e3599a2008-10-16 20:23:45 -0300403 break;
404 case GUARD_INTERVAL_1_4:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300405 val = 0x0c;
Steven Toth4e3599a2008-10-16 20:23:45 -0300406 break;
407 default:
408 dprintk("%s: invalid guard_interval\n", __func__);
409 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 }
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300411 switch (p->u.ofdm.transmission_mode) { /* mask 0x03 */
Steven Toth4e3599a2008-10-16 20:23:45 -0300412 case TRANSMISSION_MODE_2K:
Steven Toth4e3599a2008-10-16 20:23:45 -0300413 break;
414 case TRANSMISSION_MODE_8K:
Jean Delvaref89ca6f2010-09-10 10:36:37 -0300415 val |= 0x1;
Steven Toth4e3599a2008-10-16 20:23:45 -0300416 break;
417 default:
418 dprintk("%s: invalid transmission_mode\n", __func__);
419 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 }
421 cx22702_writereg(state, 0x08, val);
Steven Toth4e3599a2008-10-16 20:23:45 -0300422 cx22702_writereg(state, 0x0B,
423 (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02);
424 cx22702_writereg(state, 0x0C,
425 (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300427 /* Begin channel acquisition */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 cx22702_writereg(state, 0x00, 0x01);
429
430 return 0;
431}
432
433/* Reset the demod hardware and reset all of the configuration registers
434 to a default state. */
Steven Toth4e3599a2008-10-16 20:23:45 -0300435static int cx22702_init(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
437 int i;
Steven Toth4e3599a2008-10-16 20:23:45 -0300438 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
Steven Toth4e3599a2008-10-16 20:23:45 -0300440 cx22702_writereg(state, 0x00, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 msleep(10);
443
Steven Toth4e3599a2008-10-16 20:23:45 -0300444 for (i = 0; i < ARRAY_SIZE(init_tab); i += 2)
445 cx22702_writereg(state, init_tab[i], init_tab[i + 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
Steven Toth4e3599a2008-10-16 20:23:45 -0300447 cx22702_writereg(state, 0xf8, (state->config->output_mode << 1)
448 & 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
Steven Toth611900c2006-01-09 15:25:12 -0200450 cx22702_i2c_gate_ctrl(fe, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452 return 0;
453}
454
Steven Toth4e3599a2008-10-16 20:23:45 -0300455static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Steven Toth4e3599a2008-10-16 20:23:45 -0300457 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 u8 reg0A;
459 u8 reg23;
460
461 *status = 0;
462
Steven Toth4e3599a2008-10-16 20:23:45 -0300463 reg0A = cx22702_readreg(state, 0x0A);
464 reg23 = cx22702_readreg(state, 0x23);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Steven Toth4e3599a2008-10-16 20:23:45 -0300466 dprintk("%s: status demod=0x%02x agc=0x%02x\n"
467 , __func__, reg0A, reg23);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
Steven Toth4e3599a2008-10-16 20:23:45 -0300469 if (reg0A & 0x10) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 *status |= FE_HAS_LOCK;
471 *status |= FE_HAS_VITERBI;
472 *status |= FE_HAS_SYNC;
473 }
474
Steven Toth4e3599a2008-10-16 20:23:45 -0300475 if (reg0A & 0x20)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 *status |= FE_HAS_CARRIER;
477
Steven Toth4e3599a2008-10-16 20:23:45 -0300478 if (reg23 < 0xf0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 *status |= FE_HAS_SIGNAL;
480
481 return 0;
482}
483
Steven Toth4e3599a2008-10-16 20:23:45 -0300484static int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Steven Toth4e3599a2008-10-16 20:23:45 -0300486 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Steven Toth4e3599a2008-10-16 20:23:45 -0300488 if (cx22702_readreg(state, 0xE4) & 0x02) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 /* Realtime statistics */
Steven Toth4e3599a2008-10-16 20:23:45 -0300490 *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
491 | (cx22702_readreg(state, 0xDF) & 0x7F);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 } else {
493 /* Averagtine statistics */
Steven Toth4e3599a2008-10-16 20:23:45 -0300494 *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
495 | cx22702_readreg(state, 0xDF);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 }
497
498 return 0;
499}
500
Steven Toth4e3599a2008-10-16 20:23:45 -0300501static int cx22702_read_signal_strength(struct dvb_frontend *fe,
502 u16 *signal_strength)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Steven Toth4e3599a2008-10-16 20:23:45 -0300504 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Jean Delvare5b9a6f32010-09-10 10:32:21 -0300506 u16 rs_ber;
Steven Toth4e3599a2008-10-16 20:23:45 -0300507 rs_ber = cx22702_readreg(state, 0x23);
Bradley Kite1e9dadb2006-09-02 21:14:27 -0300508 *signal_strength = (rs_ber << 8) | rs_ber;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 return 0;
511}
512
Steven Toth4e3599a2008-10-16 20:23:45 -0300513static int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Steven Toth4e3599a2008-10-16 20:23:45 -0300515 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Jean Delvare5b9a6f32010-09-10 10:32:21 -0300517 u16 rs_ber;
Steven Toth4e3599a2008-10-16 20:23:45 -0300518 if (cx22702_readreg(state, 0xE4) & 0x02) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 /* Realtime statistics */
Steven Toth4e3599a2008-10-16 20:23:45 -0300520 rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
521 | (cx22702_readreg(state, 0xDF) & 0x7F);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 } else {
523 /* Averagine statistics */
Steven Toth4e3599a2008-10-16 20:23:45 -0300524 rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8
525 | cx22702_readreg(state, 0xDF);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
527 *snr = ~rs_ber;
528
529 return 0;
530}
531
Steven Toth4e3599a2008-10-16 20:23:45 -0300532static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533{
Steven Toth4e3599a2008-10-16 20:23:45 -0300534 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 u8 _ucblocks;
537
538 /* RS Uncorrectable Packet Count then reset */
Steven Toth4e3599a2008-10-16 20:23:45 -0300539 _ucblocks = cx22702_readreg(state, 0xE3);
Patrick Boettcherf46dbb02005-07-07 17:57:44 -0700540 if (state->prevUCBlocks < _ucblocks)
541 *ucblocks = (_ucblocks - state->prevUCBlocks);
542 else
543 *ucblocks = state->prevUCBlocks - _ucblocks;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 state->prevUCBlocks = _ucblocks;
545
546 return 0;
547}
548
Steven Toth4e3599a2008-10-16 20:23:45 -0300549static int cx22702_get_frontend(struct dvb_frontend *fe,
550 struct dvb_frontend_parameters *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Steven Toth4e3599a2008-10-16 20:23:45 -0300552 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Steven Toth4e3599a2008-10-16 20:23:45 -0300554 u8 reg0C = cx22702_readreg(state, 0x0C);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
Steven Toth4e3599a2008-10-16 20:23:45 -0300557 return cx22702_get_tps(state, &p->u.ofdm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}
559
Steven Toth4e3599a2008-10-16 20:23:45 -0300560static int cx22702_get_tune_settings(struct dvb_frontend *fe,
561 struct dvb_frontend_tune_settings *tune)
Patrick Boettcherf46dbb02005-07-07 17:57:44 -0700562{
563 tune->min_delay_ms = 1000;
564 return 0;
565}
566
Steven Toth4e3599a2008-10-16 20:23:45 -0300567static void cx22702_release(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Steven Toth4e3599a2008-10-16 20:23:45 -0300569 struct cx22702_state *state = fe->demodulator_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 kfree(state);
571}
572
Jean Delvarebdc6fad2010-09-10 10:35:12 -0300573static const struct dvb_frontend_ops cx22702_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Steven Toth4e3599a2008-10-16 20:23:45 -0300575struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
576 struct i2c_adapter *i2c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
Steven Toth4e3599a2008-10-16 20:23:45 -0300578 struct cx22702_state *state = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
580 /* allocate memory for the internal state */
Matthias Schwarzott084e24a2009-08-10 22:51:01 -0300581 state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL);
Patrick Boettcherf46dbb02005-07-07 17:57:44 -0700582 if (state == NULL)
583 goto error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 /* setup the state */
586 state->config = config;
587 state->i2c = i2c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589 /* check if the demod is there */
Patrick Boettcherf46dbb02005-07-07 17:57:44 -0700590 if (cx22702_readreg(state, 0x1f) != 0x3)
591 goto error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 /* create dvb_frontend */
Steven Toth4e3599a2008-10-16 20:23:45 -0300594 memcpy(&state->frontend.ops, &cx22702_ops,
595 sizeof(struct dvb_frontend_ops));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 state->frontend.demodulator_priv = state;
597 return &state->frontend;
598
599error:
600 kfree(state);
601 return NULL;
602}
Steven Toth4e3599a2008-10-16 20:23:45 -0300603EXPORT_SYMBOL(cx22702_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Jean Delvarebdc6fad2010-09-10 10:35:12 -0300605static const struct dvb_frontend_ops cx22702_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 .info = {
608 .name = "Conexant CX22702 DVB-T",
609 .type = FE_OFDM,
610 .frequency_min = 177000000,
611 .frequency_max = 858000000,
612 .frequency_stepsize = 166666,
613 .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
614 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
615 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
616 FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
617 FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
618 },
619
620 .release = cx22702_release,
621
622 .init = cx22702_init,
Andrew de Quincey02444222006-04-18 17:47:09 -0300623 .i2c_gate_ctrl = cx22702_i2c_gate_ctrl,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 .set_frontend = cx22702_set_tps,
626 .get_frontend = cx22702_get_frontend,
Patrick Boettcherf46dbb02005-07-07 17:57:44 -0700627 .get_tune_settings = cx22702_get_tune_settings,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629 .read_status = cx22702_read_status,
630 .read_ber = cx22702_read_ber,
631 .read_signal_strength = cx22702_read_signal_strength,
632 .read_snr = cx22702_read_snr,
633 .read_ucblocks = cx22702_read_ucblocks,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634};
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
637MODULE_AUTHOR("Steven Toth");
638MODULE_LICENSE("GPL");