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
,
125 struct dtv_frontend_properties
*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", __func__
);
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 ((int)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
->modulation
!= QPSK
&&
151 p
->modulation
!= QAM_16
&&
152 p
->modulation
!= QAM_64
)
155 if ((int)p
->hierarchy
< HIERARCHY_NONE
||
156 p
->hierarchy
> HIERARCHY_4
)
159 if (p
->bandwidth_hz
> 8000000 || p
->bandwidth_hz
< 6000000)
162 if (p
->bandwidth_hz
== 7000000)
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
->modulation
- QPSK
];
168 val
|= p
->hierarchy
- 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 acquisition */
188 static int cx22700_get_tps(struct cx22700_state
*state
,
189 struct dtv_frontend_properties
*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", __func__
);
198 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
201 val
= cx22700_readreg (state
, 0x01);
204 p
->hierarchy
= HIERARCHY_AUTO
;
206 p
->hierarchy
= HIERARCHY_NONE
+ (val
& 0x7);
208 if (((val
>> 3) & 0x3) > 2)
209 p
->modulation
= QAM_AUTO
;
211 p
->modulation
= 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);
253 static int cx22700_read_status(struct dvb_frontend
* fe
, fe_status_t
* status
)
255 struct cx22700_state
* state
= fe
->demodulator_priv
;
257 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
258 | (cx22700_readreg (state
, 0x0e) << 1);
259 u8 sync
= cx22700_readreg (state
, 0x07);
264 *status
|= FE_HAS_SIGNAL
;
267 *status
|= FE_HAS_CARRIER
;
270 *status
|= FE_HAS_VITERBI
;
273 *status
|= FE_HAS_SYNC
;
276 *status
|= FE_HAS_LOCK
;
281 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
283 struct cx22700_state
* state
= fe
->demodulator_priv
;
285 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
286 cx22700_writereg (state
, 0x0c, 0x00);
291 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
293 struct cx22700_state
* state
= fe
->demodulator_priv
;
295 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
296 | (cx22700_readreg (state
, 0x0e) << 1);
297 *signal_strength
= ~rs_ber
;
302 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
304 struct cx22700_state
* state
= fe
->demodulator_priv
;
306 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
307 | (cx22700_readreg (state
, 0x0e) << 1);
313 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
315 struct cx22700_state
* state
= fe
->demodulator_priv
;
317 *ucblocks
= cx22700_readreg (state
, 0x0f);
318 cx22700_writereg (state
, 0x0f, 0x00);
323 static int cx22700_set_frontend(struct dvb_frontend
*fe
)
325 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
326 struct cx22700_state
* state
= fe
->demodulator_priv
;
328 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
329 cx22700_writereg (state
, 0x00, 0x00);
331 if (fe
->ops
.tuner_ops
.set_params
) {
332 fe
->ops
.tuner_ops
.set_params(fe
);
333 if (fe
->ops
.i2c_gate_ctrl
) fe
->ops
.i2c_gate_ctrl(fe
, 0);
336 cx22700_set_inversion(state
, c
->inversion
);
337 cx22700_set_tps(state
, c
);
338 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
339 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
344 static int cx22700_get_frontend(struct dvb_frontend
*fe
)
346 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
347 struct cx22700_state
* state
= fe
->demodulator_priv
;
348 u8 reg09
= cx22700_readreg (state
, 0x09);
350 c
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
351 return cx22700_get_tps(state
, c
);
354 static int cx22700_i2c_gate_ctrl(struct dvb_frontend
* fe
, int enable
)
356 struct cx22700_state
* state
= fe
->demodulator_priv
;
359 return cx22700_writereg(state
, 0x0a, 0x00);
361 return cx22700_writereg(state
, 0x0a, 0x01);
365 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
367 fesettings
->min_delay_ms
= 150;
368 fesettings
->step_size
= 166667;
369 fesettings
->max_drift
= 166667*2;
373 static void cx22700_release(struct dvb_frontend
* fe
)
375 struct cx22700_state
* state
= fe
->demodulator_priv
;
379 static struct dvb_frontend_ops cx22700_ops
;
381 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
382 struct i2c_adapter
* i2c
)
384 struct cx22700_state
* state
= NULL
;
386 /* allocate memory for the internal state */
387 state
= kzalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
388 if (state
== NULL
) goto error
;
390 /* setup the state */
391 state
->config
= config
;
394 /* check if the demod is there */
395 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
397 /* create dvb_frontend */
398 memcpy(&state
->frontend
.ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
399 state
->frontend
.demodulator_priv
= state
;
400 return &state
->frontend
;
407 static struct dvb_frontend_ops cx22700_ops
= {
408 .delsys
= { SYS_DVBT
},
410 .name
= "Conexant CX22700 DVB-T",
411 .frequency_min
= 470000000,
412 .frequency_max
= 860000000,
413 .frequency_stepsize
= 166667,
414 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
415 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
416 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
420 .release
= cx22700_release
,
422 .init
= cx22700_init
,
423 .i2c_gate_ctrl
= cx22700_i2c_gate_ctrl
,
425 .set_frontend
= cx22700_set_frontend
,
426 .get_frontend
= cx22700_get_frontend
,
427 .get_tune_settings
= cx22700_get_tune_settings
,
429 .read_status
= cx22700_read_status
,
430 .read_ber
= cx22700_read_ber
,
431 .read_signal_strength
= cx22700_read_signal_strength
,
432 .read_snr
= cx22700_read_snr
,
433 .read_ucblocks
= cx22700_read_ucblocks
,
436 module_param(debug
, int, 0644);
437 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
439 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
440 MODULE_AUTHOR("Holger Waechtler");
441 MODULE_LICENSE("GPL");
443 EXPORT_SYMBOL(cx22700_attach
);