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 <media/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 if (p
->code_rate_HP
- FEC_1_2
>= sizeof(fec_tab
) ||
173 p
->code_rate_LP
- FEC_1_2
>= sizeof(fec_tab
))
175 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
176 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
178 cx22700_writereg (state
, 0x05, val
);
180 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
181 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
183 cx22700_writereg (state
, 0x06, val
);
185 cx22700_writereg (state
, 0x08, 0x04 | 0x02); /* use user tps parameters */
186 cx22700_writereg (state
, 0x08, 0x04); /* restart acquisition */
191 static int cx22700_get_tps(struct cx22700_state
*state
,
192 struct dtv_frontend_properties
*p
)
194 static const enum fe_modulation qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
195 static const enum fe_code_rate fec_tab
[5] = {
196 FEC_1_2
, FEC_2_3
, FEC_3_4
, FEC_5_6
, FEC_7_8
200 dprintk ("%s\n", __func__
);
202 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
205 val
= cx22700_readreg (state
, 0x01);
208 p
->hierarchy
= HIERARCHY_AUTO
;
210 p
->hierarchy
= HIERARCHY_NONE
+ (val
& 0x7);
212 if (((val
>> 3) & 0x3) > 2)
213 p
->modulation
= QAM_AUTO
;
215 p
->modulation
= qam_tab
[(val
>> 3) & 0x3];
217 val
= cx22700_readreg (state
, 0x02);
219 if (((val
>> 3) & 0x07) > 4)
220 p
->code_rate_HP
= FEC_AUTO
;
222 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
224 if ((val
& 0x07) > 4)
225 p
->code_rate_LP
= FEC_AUTO
;
227 p
->code_rate_LP
= fec_tab
[val
& 0x07];
229 val
= cx22700_readreg (state
, 0x03);
231 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
232 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
237 static int cx22700_init (struct dvb_frontend
* fe
)
239 { struct cx22700_state
* state
= fe
->demodulator_priv
;
242 dprintk("cx22700_init: init chip\n");
244 cx22700_writereg (state
, 0x00, 0x02); /* soft reset */
245 cx22700_writereg (state
, 0x00, 0x00);
249 for (i
=0; i
<sizeof(init_tab
); i
+=2)
250 cx22700_writereg (state
, init_tab
[i
], init_tab
[i
+1]);
252 cx22700_writereg (state
, 0x00, 0x01);
257 static int cx22700_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
259 struct cx22700_state
* state
= fe
->demodulator_priv
;
261 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
262 | (cx22700_readreg (state
, 0x0e) << 1);
263 u8 sync
= cx22700_readreg (state
, 0x07);
268 *status
|= FE_HAS_SIGNAL
;
271 *status
|= FE_HAS_CARRIER
;
274 *status
|= FE_HAS_VITERBI
;
277 *status
|= FE_HAS_SYNC
;
280 *status
|= FE_HAS_LOCK
;
285 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
287 struct cx22700_state
* state
= fe
->demodulator_priv
;
289 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
290 cx22700_writereg (state
, 0x0c, 0x00);
295 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
297 struct cx22700_state
* state
= fe
->demodulator_priv
;
299 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
300 | (cx22700_readreg (state
, 0x0e) << 1);
301 *signal_strength
= ~rs_ber
;
306 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
308 struct cx22700_state
* state
= fe
->demodulator_priv
;
310 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
311 | (cx22700_readreg (state
, 0x0e) << 1);
317 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
319 struct cx22700_state
* state
= fe
->demodulator_priv
;
321 *ucblocks
= cx22700_readreg (state
, 0x0f);
322 cx22700_writereg (state
, 0x0f, 0x00);
327 static int cx22700_set_frontend(struct dvb_frontend
*fe
)
329 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
330 struct cx22700_state
* state
= fe
->demodulator_priv
;
332 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
333 cx22700_writereg (state
, 0x00, 0x00);
335 if (fe
->ops
.tuner_ops
.set_params
) {
336 fe
->ops
.tuner_ops
.set_params(fe
);
337 if (fe
->ops
.i2c_gate_ctrl
) fe
->ops
.i2c_gate_ctrl(fe
, 0);
340 cx22700_set_inversion(state
, c
->inversion
);
341 cx22700_set_tps(state
, c
);
342 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
343 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
348 static int cx22700_get_frontend(struct dvb_frontend
*fe
,
349 struct dtv_frontend_properties
*c
)
351 struct cx22700_state
* state
= fe
->demodulator_priv
;
352 u8 reg09
= cx22700_readreg (state
, 0x09);
354 c
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
355 return cx22700_get_tps(state
, c
);
358 static int cx22700_i2c_gate_ctrl(struct dvb_frontend
* fe
, int enable
)
360 struct cx22700_state
* state
= fe
->demodulator_priv
;
363 return cx22700_writereg(state
, 0x0a, 0x00);
365 return cx22700_writereg(state
, 0x0a, 0x01);
369 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
371 fesettings
->min_delay_ms
= 150;
372 fesettings
->step_size
= 166667;
373 fesettings
->max_drift
= 166667*2;
377 static void cx22700_release(struct dvb_frontend
* fe
)
379 struct cx22700_state
* state
= fe
->demodulator_priv
;
383 static const struct dvb_frontend_ops cx22700_ops
;
385 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
386 struct i2c_adapter
* i2c
)
388 struct cx22700_state
* state
= NULL
;
390 /* allocate memory for the internal state */
391 state
= kzalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
392 if (state
== NULL
) goto error
;
394 /* setup the state */
395 state
->config
= config
;
398 /* check if the demod is there */
399 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
401 /* create dvb_frontend */
402 memcpy(&state
->frontend
.ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
403 state
->frontend
.demodulator_priv
= state
;
404 return &state
->frontend
;
411 static const struct dvb_frontend_ops cx22700_ops
= {
412 .delsys
= { SYS_DVBT
},
414 .name
= "Conexant CX22700 DVB-T",
415 .frequency_min
= 470000000,
416 .frequency_max
= 860000000,
417 .frequency_stepsize
= 166667,
418 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
419 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
420 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
424 .release
= cx22700_release
,
426 .init
= cx22700_init
,
427 .i2c_gate_ctrl
= cx22700_i2c_gate_ctrl
,
429 .set_frontend
= cx22700_set_frontend
,
430 .get_frontend
= cx22700_get_frontend
,
431 .get_tune_settings
= cx22700_get_tune_settings
,
433 .read_status
= cx22700_read_status
,
434 .read_ber
= cx22700_read_ber
,
435 .read_signal_strength
= cx22700_read_signal_strength
,
436 .read_snr
= cx22700_read_snr
,
437 .read_ucblocks
= cx22700_read_ucblocks
,
440 module_param(debug
, int, 0644);
441 MODULE_PARM_DESC(debug
, "Turn on/off frontend debugging (default:off).");
443 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
444 MODULE_AUTHOR("Holger Waechtler");
445 MODULE_LICENSE("GPL");
447 EXPORT_SYMBOL(cx22700_attach
);