2 Conexant cx22700 DVB OFDM demodulator driver
4 Copyright (C) 2001-2002 Convergence Integrated Media GmbH
5 Holger Waechtler <holger@convergence.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/string.h>
28 #include <linux/slab.h>
29 #include "dvb_frontend.h"
33 struct cx22700_state
{
35 struct i2c_adapter
* i2c
;
37 struct dvb_frontend_ops ops
;
39 const struct cx22700_config
* config
;
41 struct dvb_frontend frontend
;
46 #define dprintk(args...) \
48 if (debug) printk(KERN_DEBUG "cx22700: " args); \
51 static u8 init_tab
[] = {
73 static int cx22700_writereg (struct cx22700_state
* state
, u8 reg
, u8 data
)
76 u8 buf
[] = { reg
, data
};
77 struct i2c_msg msg
= { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= buf
, .len
= 2 };
79 dprintk ("%s\n", __FUNCTION__
);
81 ret
= i2c_transfer (state
->i2c
, &msg
, 1);
84 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
85 __FUNCTION__
, reg
, data
, ret
);
87 return (ret
!= 1) ? -1 : 0;
90 static int cx22700_readreg (struct cx22700_state
* state
, u8 reg
)
95 struct i2c_msg msg
[] = { { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= b0
, .len
= 1 },
96 { .addr
= state
->config
->demod_address
, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
98 dprintk ("%s\n", __FUNCTION__
);
100 ret
= i2c_transfer (state
->i2c
, msg
, 2);
102 if (ret
!= 2) return -EIO
;
107 static int cx22700_set_inversion (struct cx22700_state
* state
, int inversion
)
111 dprintk ("%s\n", __FUNCTION__
);
117 val
= cx22700_readreg (state
, 0x09);
118 return cx22700_writereg (state
, 0x09, val
| 0x01);
120 val
= cx22700_readreg (state
, 0x09);
121 return cx22700_writereg (state
, 0x09, val
& 0xfe);
127 static int cx22700_set_tps (struct cx22700_state
*state
, struct dvb_ofdm_parameters
*p
)
129 static const u8 qam_tab
[4] = { 0, 1, 0, 2 };
130 static const u8 fec_tab
[6] = { 0, 1, 2, 0, 3, 4 };
133 dprintk ("%s\n", __FUNCTION__
);
135 if (p
->code_rate_HP
< FEC_1_2
|| p
->code_rate_HP
> FEC_7_8
)
138 if (p
->code_rate_LP
< FEC_1_2
|| p
->code_rate_LP
> FEC_7_8
)
140 if (p
->code_rate_HP
== FEC_4_5
|| p
->code_rate_LP
== FEC_4_5
)
143 if (p
->guard_interval
< GUARD_INTERVAL_1_32
||
144 p
->guard_interval
> GUARD_INTERVAL_1_4
)
147 if (p
->transmission_mode
!= TRANSMISSION_MODE_2K
&&
148 p
->transmission_mode
!= TRANSMISSION_MODE_8K
)
151 if (p
->constellation
!= QPSK
&&
152 p
->constellation
!= QAM_16
&&
153 p
->constellation
!= QAM_64
)
156 if (p
->hierarchy_information
< HIERARCHY_NONE
||
157 p
->hierarchy_information
> HIERARCHY_4
)
160 if (p
->bandwidth
< BANDWIDTH_8_MHZ
&& p
->bandwidth
> BANDWIDTH_6_MHZ
)
163 if (p
->bandwidth
== BANDWIDTH_7_MHZ
)
164 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 | 0x10));
166 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 & ~0x10));
168 val
= qam_tab
[p
->constellation
- QPSK
];
169 val
|= p
->hierarchy_information
- HIERARCHY_NONE
;
171 cx22700_writereg (state
, 0x04, val
);
173 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
174 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
176 cx22700_writereg (state
, 0x05, val
);
178 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
179 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
181 cx22700_writereg (state
, 0x06, val
);
183 cx22700_writereg (state
, 0x08, 0x04 | 0x02); /* use user tps parameters */
184 cx22700_writereg (state
, 0x08, 0x04); /* restart aquisition */
189 static int cx22700_get_tps (struct cx22700_state
* state
, struct dvb_ofdm_parameters
*p
)
191 static const fe_modulation_t qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
192 static const fe_code_rate_t fec_tab
[5] = { FEC_1_2
, FEC_2_3
, FEC_3_4
,
196 dprintk ("%s\n", __FUNCTION__
);
198 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
201 val
= cx22700_readreg (state
, 0x01);
204 p
->hierarchy_information
= HIERARCHY_AUTO
;
206 p
->hierarchy_information
= HIERARCHY_NONE
+ (val
& 0x7);
208 if (((val
>> 3) & 0x3) > 2)
209 p
->constellation
= QAM_AUTO
;
211 p
->constellation
= qam_tab
[(val
>> 3) & 0x3];
213 val
= cx22700_readreg (state
, 0x02);
215 if (((val
>> 3) & 0x07) > 4)
216 p
->code_rate_HP
= FEC_AUTO
;
218 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
220 if ((val
& 0x07) > 4)
221 p
->code_rate_LP
= FEC_AUTO
;
223 p
->code_rate_LP
= fec_tab
[val
& 0x07];
225 val
= cx22700_readreg (state
, 0x03);
227 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
228 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
233 static int cx22700_init (struct dvb_frontend
* fe
)
235 { struct cx22700_state
* state
= fe
->demodulator_priv
;
238 dprintk("cx22700_init: init chip\n");
240 cx22700_writereg (state
, 0x00, 0x02); /* soft reset */
241 cx22700_writereg (state
, 0x00, 0x00);
245 for (i
=0; i
<sizeof(init_tab
); i
+=2)
246 cx22700_writereg (state
, init_tab
[i
], init_tab
[i
+1]);
248 cx22700_writereg (state
, 0x00, 0x01);
250 if (state
->config
->pll_init
) {
251 cx22700_writereg (state
, 0x0a, 0x00); /* open i2c bus switch */
252 state
->config
->pll_init(fe
);
253 cx22700_writereg (state
, 0x0a, 0x01); /* close i2c bus switch */
259 static int cx22700_read_status(struct dvb_frontend
* fe
, fe_status_t
* status
)
261 struct cx22700_state
* state
= fe
->demodulator_priv
;
263 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
264 | (cx22700_readreg (state
, 0x0e) << 1);
265 u8 sync
= cx22700_readreg (state
, 0x07);
270 *status
|= FE_HAS_SIGNAL
;
273 *status
|= FE_HAS_CARRIER
;
276 *status
|= FE_HAS_VITERBI
;
279 *status
|= FE_HAS_SYNC
;
282 *status
|= FE_HAS_LOCK
;
287 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
289 struct cx22700_state
* state
= fe
->demodulator_priv
;
291 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
292 cx22700_writereg (state
, 0x0c, 0x00);
297 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
299 struct cx22700_state
* state
= fe
->demodulator_priv
;
301 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
302 | (cx22700_readreg (state
, 0x0e) << 1);
303 *signal_strength
= ~rs_ber
;
308 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
310 struct cx22700_state
* state
= fe
->demodulator_priv
;
312 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
313 | (cx22700_readreg (state
, 0x0e) << 1);
319 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
321 struct cx22700_state
* state
= fe
->demodulator_priv
;
323 *ucblocks
= cx22700_readreg (state
, 0x0f);
324 cx22700_writereg (state
, 0x0f, 0x00);
329 static int cx22700_set_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
331 struct cx22700_state
* state
= fe
->demodulator_priv
;
333 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
334 cx22700_writereg (state
, 0x00, 0x00);
336 cx22700_writereg (state
, 0x0a, 0x00); /* open i2c bus switch */
337 state
->config
->pll_set(fe
, p
);
338 cx22700_writereg (state
, 0x0a, 0x01); /* close i2c bus switch */
339 cx22700_set_inversion (state
, p
->inversion
);
340 cx22700_set_tps (state
, &p
->u
.ofdm
);
341 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
342 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
347 static int cx22700_get_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
349 struct cx22700_state
* state
= fe
->demodulator_priv
;
350 u8 reg09
= cx22700_readreg (state
, 0x09);
352 p
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
353 return cx22700_get_tps (state
, &p
->u
.ofdm
);
356 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
358 fesettings
->min_delay_ms
= 150;
359 fesettings
->step_size
= 166667;
360 fesettings
->max_drift
= 166667*2;
364 static void cx22700_release(struct dvb_frontend
* fe
)
366 struct cx22700_state
* state
= fe
->demodulator_priv
;
370 static struct dvb_frontend_ops cx22700_ops
;
372 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
373 struct i2c_adapter
* i2c
)
375 struct cx22700_state
* state
= NULL
;
377 /* allocate memory for the internal state */
378 state
= kmalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
379 if (state
== NULL
) goto error
;
381 /* setup the state */
382 state
->config
= config
;
384 memcpy(&state
->ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
386 /* check if the demod is there */
387 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
389 /* create dvb_frontend */
390 state
->frontend
.ops
= &state
->ops
;
391 state
->frontend
.demodulator_priv
= state
;
392 return &state
->frontend
;
399 static struct dvb_frontend_ops cx22700_ops
= {
402 .name
= "Conexant CX22700 DVB-T",
404 .frequency_min
= 470000000,
405 .frequency_max
= 860000000,
406 .frequency_stepsize
= 166667,
407 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
408 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
409 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
413 .release
= cx22700_release
,
415 .init
= cx22700_init
,
417 .set_frontend
= cx22700_set_frontend
,
418 .get_frontend
= cx22700_get_frontend
,
419 .get_tune_settings
= cx22700_get_tune_settings
,
421 .read_status
= cx22700_read_status
,
422 .read_ber
= cx22700_read_ber
,
423 .read_signal_strength
= cx22700_read_signal_strength
,
424 .read_snr
= cx22700_read_snr
,
425 .read_ucblocks
= cx22700_read_ucblocks
,
428 module_param(debug
, int, 0644);
429 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
431 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
432 MODULE_AUTHOR("Holger Waechtler");
433 MODULE_LICENSE("GPL");
435 EXPORT_SYMBOL(cx22700_attach
);