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 const struct cx22700_config
* config
;
39 struct dvb_frontend frontend
;
44 #define dprintk(args...) \
46 if (debug) printk(KERN_DEBUG "cx22700: " args); \
49 static u8 init_tab
[] = {
71 static int cx22700_writereg (struct cx22700_state
* state
, u8 reg
, u8 data
)
74 u8 buf
[] = { reg
, data
};
75 struct i2c_msg msg
= { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= buf
, .len
= 2 };
77 dprintk ("%s\n", __FUNCTION__
);
79 ret
= i2c_transfer (state
->i2c
, &msg
, 1);
82 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
83 __FUNCTION__
, reg
, data
, ret
);
85 return (ret
!= 1) ? -1 : 0;
88 static int cx22700_readreg (struct cx22700_state
* state
, u8 reg
)
93 struct i2c_msg msg
[] = { { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= b0
, .len
= 1 },
94 { .addr
= state
->config
->demod_address
, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
96 dprintk ("%s\n", __FUNCTION__
);
98 ret
= i2c_transfer (state
->i2c
, msg
, 2);
100 if (ret
!= 2) return -EIO
;
105 static int cx22700_set_inversion (struct cx22700_state
* state
, int inversion
)
109 dprintk ("%s\n", __FUNCTION__
);
115 val
= cx22700_readreg (state
, 0x09);
116 return cx22700_writereg (state
, 0x09, val
| 0x01);
118 val
= cx22700_readreg (state
, 0x09);
119 return cx22700_writereg (state
, 0x09, val
& 0xfe);
125 static int cx22700_set_tps (struct cx22700_state
*state
, struct dvb_ofdm_parameters
*p
)
127 static const u8 qam_tab
[4] = { 0, 1, 0, 2 };
128 static const u8 fec_tab
[6] = { 0, 1, 2, 0, 3, 4 };
131 dprintk ("%s\n", __FUNCTION__
);
133 if (p
->code_rate_HP
< FEC_1_2
|| p
->code_rate_HP
> FEC_7_8
)
136 if (p
->code_rate_LP
< FEC_1_2
|| p
->code_rate_LP
> FEC_7_8
)
139 if (p
->code_rate_HP
== FEC_4_5
|| p
->code_rate_LP
== FEC_4_5
)
142 if (p
->guard_interval
< GUARD_INTERVAL_1_32
||
143 p
->guard_interval
> GUARD_INTERVAL_1_4
)
146 if (p
->transmission_mode
!= TRANSMISSION_MODE_2K
&&
147 p
->transmission_mode
!= TRANSMISSION_MODE_8K
)
150 if (p
->constellation
!= QPSK
&&
151 p
->constellation
!= QAM_16
&&
152 p
->constellation
!= QAM_64
)
155 if (p
->hierarchy_information
< HIERARCHY_NONE
||
156 p
->hierarchy_information
> HIERARCHY_4
)
159 if (p
->bandwidth
< BANDWIDTH_8_MHZ
&& p
->bandwidth
> BANDWIDTH_6_MHZ
)
162 if (p
->bandwidth
== BANDWIDTH_7_MHZ
)
163 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 | 0x10));
165 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 & ~0x10));
167 val
= qam_tab
[p
->constellation
- QPSK
];
168 val
|= p
->hierarchy_information
- HIERARCHY_NONE
;
170 cx22700_writereg (state
, 0x04, val
);
172 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
173 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
175 cx22700_writereg (state
, 0x05, val
);
177 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
178 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
180 cx22700_writereg (state
, 0x06, val
);
182 cx22700_writereg (state
, 0x08, 0x04 | 0x02); /* use user tps parameters */
183 cx22700_writereg (state
, 0x08, 0x04); /* restart aquisition */
188 static int cx22700_get_tps (struct cx22700_state
* state
, struct dvb_ofdm_parameters
*p
)
190 static const fe_modulation_t qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
191 static const fe_code_rate_t fec_tab
[5] = { FEC_1_2
, FEC_2_3
, FEC_3_4
,
195 dprintk ("%s\n", __FUNCTION__
);
197 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
200 val
= cx22700_readreg (state
, 0x01);
203 p
->hierarchy_information
= HIERARCHY_AUTO
;
205 p
->hierarchy_information
= HIERARCHY_NONE
+ (val
& 0x7);
207 if (((val
>> 3) & 0x3) > 2)
208 p
->constellation
= QAM_AUTO
;
210 p
->constellation
= qam_tab
[(val
>> 3) & 0x3];
212 val
= cx22700_readreg (state
, 0x02);
214 if (((val
>> 3) & 0x07) > 4)
215 p
->code_rate_HP
= FEC_AUTO
;
217 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
219 if ((val
& 0x07) > 4)
220 p
->code_rate_LP
= FEC_AUTO
;
222 p
->code_rate_LP
= fec_tab
[val
& 0x07];
224 val
= cx22700_readreg (state
, 0x03);
226 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
227 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
232 static int cx22700_init (struct dvb_frontend
* fe
)
234 { struct cx22700_state
* state
= fe
->demodulator_priv
;
237 dprintk("cx22700_init: init chip\n");
239 cx22700_writereg (state
, 0x00, 0x02); /* soft reset */
240 cx22700_writereg (state
, 0x00, 0x00);
244 for (i
=0; i
<sizeof(init_tab
); i
+=2)
245 cx22700_writereg (state
, init_tab
[i
], init_tab
[i
+1]);
247 cx22700_writereg (state
, 0x00, 0x01);
252 static int cx22700_read_status(struct dvb_frontend
* fe
, fe_status_t
* status
)
254 struct cx22700_state
* state
= fe
->demodulator_priv
;
256 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
257 | (cx22700_readreg (state
, 0x0e) << 1);
258 u8 sync
= cx22700_readreg (state
, 0x07);
263 *status
|= FE_HAS_SIGNAL
;
266 *status
|= FE_HAS_CARRIER
;
269 *status
|= FE_HAS_VITERBI
;
272 *status
|= FE_HAS_SYNC
;
275 *status
|= FE_HAS_LOCK
;
280 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
282 struct cx22700_state
* state
= fe
->demodulator_priv
;
284 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
285 cx22700_writereg (state
, 0x0c, 0x00);
290 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
292 struct cx22700_state
* state
= fe
->demodulator_priv
;
294 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
295 | (cx22700_readreg (state
, 0x0e) << 1);
296 *signal_strength
= ~rs_ber
;
301 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
303 struct cx22700_state
* state
= fe
->demodulator_priv
;
305 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
306 | (cx22700_readreg (state
, 0x0e) << 1);
312 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
314 struct cx22700_state
* state
= fe
->demodulator_priv
;
316 *ucblocks
= cx22700_readreg (state
, 0x0f);
317 cx22700_writereg (state
, 0x0f, 0x00);
322 static int cx22700_set_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
324 struct cx22700_state
* state
= fe
->demodulator_priv
;
326 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
327 cx22700_writereg (state
, 0x00, 0x00);
329 if (fe
->ops
.tuner_ops
.set_params
) {
330 fe
->ops
.tuner_ops
.set_params(fe
, p
);
331 if (fe
->ops
.i2c_gate_ctrl
) fe
->ops
.i2c_gate_ctrl(fe
, 0);
334 cx22700_set_inversion (state
, p
->inversion
);
335 cx22700_set_tps (state
, &p
->u
.ofdm
);
336 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
337 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
342 static int cx22700_get_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
344 struct cx22700_state
* state
= fe
->demodulator_priv
;
345 u8 reg09
= cx22700_readreg (state
, 0x09);
347 p
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
348 return cx22700_get_tps (state
, &p
->u
.ofdm
);
351 static int cx22700_i2c_gate_ctrl(struct dvb_frontend
* fe
, int enable
)
353 struct cx22700_state
* state
= fe
->demodulator_priv
;
356 return cx22700_writereg(state
, 0x0a, 0x00);
358 return cx22700_writereg(state
, 0x0a, 0x01);
362 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
364 fesettings
->min_delay_ms
= 150;
365 fesettings
->step_size
= 166667;
366 fesettings
->max_drift
= 166667*2;
370 static void cx22700_release(struct dvb_frontend
* fe
)
372 struct cx22700_state
* state
= fe
->demodulator_priv
;
376 static struct dvb_frontend_ops cx22700_ops
;
378 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
379 struct i2c_adapter
* i2c
)
381 struct cx22700_state
* state
= NULL
;
383 /* allocate memory for the internal state */
384 state
= kmalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
385 if (state
== NULL
) goto error
;
387 /* setup the state */
388 state
->config
= config
;
391 /* check if the demod is there */
392 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
394 /* create dvb_frontend */
395 memcpy(&state
->frontend
.ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
396 state
->frontend
.demodulator_priv
= state
;
397 return &state
->frontend
;
404 static struct dvb_frontend_ops cx22700_ops
= {
407 .name
= "Conexant CX22700 DVB-T",
409 .frequency_min
= 470000000,
410 .frequency_max
= 860000000,
411 .frequency_stepsize
= 166667,
412 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
413 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
414 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
418 .release
= cx22700_release
,
420 .init
= cx22700_init
,
421 .i2c_gate_ctrl
= cx22700_i2c_gate_ctrl
,
423 .set_frontend
= cx22700_set_frontend
,
424 .get_frontend
= cx22700_get_frontend
,
425 .get_tune_settings
= cx22700_get_tune_settings
,
427 .read_status
= cx22700_read_status
,
428 .read_ber
= cx22700_read_ber
,
429 .read_signal_strength
= cx22700_read_signal_strength
,
430 .read_snr
= cx22700_read_snr
,
431 .read_ucblocks
= cx22700_read_ucblocks
,
434 module_param(debug
, int, 0644);
435 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
437 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
438 MODULE_AUTHOR("Holger Waechtler");
439 MODULE_LICENSE("GPL");
441 EXPORT_SYMBOL(cx22700_attach
);