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/string.h>
27 #include <linux/slab.h>
28 #include "dvb_frontend.h"
32 struct cx22700_state
{
34 struct i2c_adapter
* i2c
;
36 const struct cx22700_config
* config
;
38 struct dvb_frontend frontend
;
43 #define dprintk(args...) \
45 if (debug) printk(KERN_DEBUG "cx22700: " args); \
48 static u8 init_tab
[] = {
70 static int cx22700_writereg (struct cx22700_state
* state
, u8 reg
, u8 data
)
73 u8 buf
[] = { reg
, data
};
74 struct i2c_msg msg
= { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= buf
, .len
= 2 };
76 dprintk ("%s\n", __func__
);
78 ret
= i2c_transfer (state
->i2c
, &msg
, 1);
81 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
82 __func__
, reg
, data
, ret
);
84 return (ret
!= 1) ? -1 : 0;
87 static int cx22700_readreg (struct cx22700_state
* state
, u8 reg
)
92 struct i2c_msg msg
[] = { { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= b0
, .len
= 1 },
93 { .addr
= state
->config
->demod_address
, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
95 dprintk ("%s\n", __func__
);
97 ret
= i2c_transfer (state
->i2c
, msg
, 2);
99 if (ret
!= 2) return -EIO
;
104 static int cx22700_set_inversion (struct cx22700_state
* state
, int inversion
)
108 dprintk ("%s\n", __func__
);
114 val
= cx22700_readreg (state
, 0x09);
115 return cx22700_writereg (state
, 0x09, val
| 0x01);
117 val
= cx22700_readreg (state
, 0x09);
118 return cx22700_writereg (state
, 0x09, val
& 0xfe);
124 static int cx22700_set_tps (struct cx22700_state
*state
, struct dvb_ofdm_parameters
*p
)
126 static const u8 qam_tab
[4] = { 0, 1, 0, 2 };
127 static const u8 fec_tab
[6] = { 0, 1, 2, 0, 3, 4 };
130 dprintk ("%s\n", __func__
);
132 if (p
->code_rate_HP
< FEC_1_2
|| p
->code_rate_HP
> FEC_7_8
)
135 if (p
->code_rate_LP
< FEC_1_2
|| p
->code_rate_LP
> FEC_7_8
)
138 if (p
->code_rate_HP
== FEC_4_5
|| p
->code_rate_LP
== FEC_4_5
)
141 if (p
->guard_interval
< GUARD_INTERVAL_1_32
||
142 p
->guard_interval
> GUARD_INTERVAL_1_4
)
145 if (p
->transmission_mode
!= TRANSMISSION_MODE_2K
&&
146 p
->transmission_mode
!= TRANSMISSION_MODE_8K
)
149 if (p
->constellation
!= QPSK
&&
150 p
->constellation
!= QAM_16
&&
151 p
->constellation
!= QAM_64
)
154 if (p
->hierarchy_information
< HIERARCHY_NONE
||
155 p
->hierarchy_information
> HIERARCHY_4
)
158 if (p
->bandwidth
< BANDWIDTH_8_MHZ
|| p
->bandwidth
> BANDWIDTH_6_MHZ
)
161 if (p
->bandwidth
== BANDWIDTH_7_MHZ
)
162 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 | 0x10));
164 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 & ~0x10));
166 val
= qam_tab
[p
->constellation
- QPSK
];
167 val
|= p
->hierarchy_information
- HIERARCHY_NONE
;
169 cx22700_writereg (state
, 0x04, val
);
171 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
172 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
174 cx22700_writereg (state
, 0x05, val
);
176 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
177 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
179 cx22700_writereg (state
, 0x06, val
);
181 cx22700_writereg (state
, 0x08, 0x04 | 0x02); /* use user tps parameters */
182 cx22700_writereg (state
, 0x08, 0x04); /* restart aquisition */
187 static int cx22700_get_tps (struct cx22700_state
* state
, struct dvb_ofdm_parameters
*p
)
189 static const fe_modulation_t qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
190 static const fe_code_rate_t fec_tab
[5] = { FEC_1_2
, FEC_2_3
, FEC_3_4
,
194 dprintk ("%s\n", __func__
);
196 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
199 val
= cx22700_readreg (state
, 0x01);
202 p
->hierarchy_information
= HIERARCHY_AUTO
;
204 p
->hierarchy_information
= HIERARCHY_NONE
+ (val
& 0x7);
206 if (((val
>> 3) & 0x3) > 2)
207 p
->constellation
= QAM_AUTO
;
209 p
->constellation
= qam_tab
[(val
>> 3) & 0x3];
211 val
= cx22700_readreg (state
, 0x02);
213 if (((val
>> 3) & 0x07) > 4)
214 p
->code_rate_HP
= FEC_AUTO
;
216 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
218 if ((val
& 0x07) > 4)
219 p
->code_rate_LP
= FEC_AUTO
;
221 p
->code_rate_LP
= fec_tab
[val
& 0x07];
223 val
= cx22700_readreg (state
, 0x03);
225 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
226 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
231 static int cx22700_init (struct dvb_frontend
* fe
)
233 { struct cx22700_state
* state
= fe
->demodulator_priv
;
236 dprintk("cx22700_init: init chip\n");
238 cx22700_writereg (state
, 0x00, 0x02); /* soft reset */
239 cx22700_writereg (state
, 0x00, 0x00);
243 for (i
=0; i
<sizeof(init_tab
); i
+=2)
244 cx22700_writereg (state
, init_tab
[i
], init_tab
[i
+1]);
246 cx22700_writereg (state
, 0x00, 0x01);
251 static int cx22700_read_status(struct dvb_frontend
* fe
, fe_status_t
* status
)
253 struct cx22700_state
* state
= fe
->demodulator_priv
;
255 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
256 | (cx22700_readreg (state
, 0x0e) << 1);
257 u8 sync
= cx22700_readreg (state
, 0x07);
262 *status
|= FE_HAS_SIGNAL
;
265 *status
|= FE_HAS_CARRIER
;
268 *status
|= FE_HAS_VITERBI
;
271 *status
|= FE_HAS_SYNC
;
274 *status
|= FE_HAS_LOCK
;
279 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
281 struct cx22700_state
* state
= fe
->demodulator_priv
;
283 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
284 cx22700_writereg (state
, 0x0c, 0x00);
289 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
291 struct cx22700_state
* state
= fe
->demodulator_priv
;
293 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
294 | (cx22700_readreg (state
, 0x0e) << 1);
295 *signal_strength
= ~rs_ber
;
300 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
302 struct cx22700_state
* state
= fe
->demodulator_priv
;
304 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
305 | (cx22700_readreg (state
, 0x0e) << 1);
311 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
313 struct cx22700_state
* state
= fe
->demodulator_priv
;
315 *ucblocks
= cx22700_readreg (state
, 0x0f);
316 cx22700_writereg (state
, 0x0f, 0x00);
321 static int cx22700_set_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
323 struct cx22700_state
* state
= fe
->demodulator_priv
;
325 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
326 cx22700_writereg (state
, 0x00, 0x00);
328 if (fe
->ops
.tuner_ops
.set_params
) {
329 fe
->ops
.tuner_ops
.set_params(fe
, p
);
330 if (fe
->ops
.i2c_gate_ctrl
) fe
->ops
.i2c_gate_ctrl(fe
, 0);
333 cx22700_set_inversion (state
, p
->inversion
);
334 cx22700_set_tps (state
, &p
->u
.ofdm
);
335 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
336 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
341 static int cx22700_get_frontend(struct dvb_frontend
* fe
, struct dvb_frontend_parameters
*p
)
343 struct cx22700_state
* state
= fe
->demodulator_priv
;
344 u8 reg09
= cx22700_readreg (state
, 0x09);
346 p
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
347 return cx22700_get_tps (state
, &p
->u
.ofdm
);
350 static int cx22700_i2c_gate_ctrl(struct dvb_frontend
* fe
, int enable
)
352 struct cx22700_state
* state
= fe
->demodulator_priv
;
355 return cx22700_writereg(state
, 0x0a, 0x00);
357 return cx22700_writereg(state
, 0x0a, 0x01);
361 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
363 fesettings
->min_delay_ms
= 150;
364 fesettings
->step_size
= 166667;
365 fesettings
->max_drift
= 166667*2;
369 static void cx22700_release(struct dvb_frontend
* fe
)
371 struct cx22700_state
* state
= fe
->demodulator_priv
;
375 static struct dvb_frontend_ops cx22700_ops
;
377 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
378 struct i2c_adapter
* i2c
)
380 struct cx22700_state
* state
= NULL
;
382 /* allocate memory for the internal state */
383 state
= kzalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
384 if (state
== NULL
) goto error
;
386 /* setup the state */
387 state
->config
= config
;
390 /* check if the demod is there */
391 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
393 /* create dvb_frontend */
394 memcpy(&state
->frontend
.ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
395 state
->frontend
.demodulator_priv
= state
;
396 return &state
->frontend
;
403 static struct dvb_frontend_ops cx22700_ops
= {
406 .name
= "Conexant CX22700 DVB-T",
408 .frequency_min
= 470000000,
409 .frequency_max
= 860000000,
410 .frequency_stepsize
= 166667,
411 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
412 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
413 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
417 .release
= cx22700_release
,
419 .init
= cx22700_init
,
420 .i2c_gate_ctrl
= cx22700_i2c_gate_ctrl
,
422 .set_frontend
= cx22700_set_frontend
,
423 .get_frontend
= cx22700_get_frontend
,
424 .get_tune_settings
= cx22700_get_tune_settings
,
426 .read_status
= cx22700_read_status
,
427 .read_ber
= cx22700_read_ber
,
428 .read_signal_strength
= cx22700_read_signal_strength
,
429 .read_snr
= cx22700_read_snr
,
430 .read_ucblocks
= cx22700_read_ucblocks
,
433 module_param(debug
, int, 0644);
434 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
436 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
437 MODULE_AUTHOR("Holger Waechtler");
438 MODULE_LICENSE("GPL");
440 EXPORT_SYMBOL(cx22700_attach
);