1 // SPDX-License-Identifier: GPL-2.0-or-later
3 Conexant cx22700 DVB OFDM demodulator driver
5 Copyright (C) 2001-2002 Convergence Integrated Media GmbH
6 Holger Waechtler <holger@convergence.de>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16 #include <media/dvb_frontend.h>
20 struct cx22700_state
{
22 struct i2c_adapter
* i2c
;
24 const struct cx22700_config
* config
;
26 struct dvb_frontend frontend
;
31 #define dprintk(args...) \
33 if (debug) printk(KERN_DEBUG "cx22700: " args); \
36 static u8 init_tab
[] = {
58 static int cx22700_writereg (struct cx22700_state
* state
, u8 reg
, u8 data
)
61 u8 buf
[] = { reg
, data
};
62 struct i2c_msg msg
= { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= buf
, .len
= 2 };
64 dprintk ("%s\n", __func__
);
66 ret
= i2c_transfer (state
->i2c
, &msg
, 1);
69 printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
70 __func__
, reg
, data
, ret
);
72 return (ret
!= 1) ? -1 : 0;
75 static int cx22700_readreg (struct cx22700_state
* state
, u8 reg
)
80 struct i2c_msg msg
[] = { { .addr
= state
->config
->demod_address
, .flags
= 0, .buf
= b0
, .len
= 1 },
81 { .addr
= state
->config
->demod_address
, .flags
= I2C_M_RD
, .buf
= b1
, .len
= 1 } };
83 dprintk ("%s\n", __func__
);
85 ret
= i2c_transfer (state
->i2c
, msg
, 2);
87 if (ret
!= 2) return -EIO
;
92 static int cx22700_set_inversion (struct cx22700_state
* state
, int inversion
)
96 dprintk ("%s\n", __func__
);
102 val
= cx22700_readreg (state
, 0x09);
103 return cx22700_writereg (state
, 0x09, val
| 0x01);
105 val
= cx22700_readreg (state
, 0x09);
106 return cx22700_writereg (state
, 0x09, val
& 0xfe);
112 static int cx22700_set_tps(struct cx22700_state
*state
,
113 struct dtv_frontend_properties
*p
)
115 static const u8 qam_tab
[4] = { 0, 1, 0, 2 };
116 static const u8 fec_tab
[6] = { 0, 1, 2, 0, 3, 4 };
119 dprintk ("%s\n", __func__
);
121 if (p
->code_rate_HP
< FEC_1_2
|| p
->code_rate_HP
> FEC_7_8
)
124 if (p
->code_rate_LP
< FEC_1_2
|| p
->code_rate_LP
> FEC_7_8
)
127 if (p
->code_rate_HP
== FEC_4_5
|| p
->code_rate_LP
== FEC_4_5
)
130 if ((int)p
->guard_interval
< GUARD_INTERVAL_1_32
||
131 p
->guard_interval
> GUARD_INTERVAL_1_4
)
134 if (p
->transmission_mode
!= TRANSMISSION_MODE_2K
&&
135 p
->transmission_mode
!= TRANSMISSION_MODE_8K
)
138 if (p
->modulation
!= QPSK
&&
139 p
->modulation
!= QAM_16
&&
140 p
->modulation
!= QAM_64
)
143 if ((int)p
->hierarchy
< HIERARCHY_NONE
||
144 p
->hierarchy
> HIERARCHY_4
)
147 if (p
->bandwidth_hz
> 8000000 || p
->bandwidth_hz
< 6000000)
150 if (p
->bandwidth_hz
== 7000000)
151 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 | 0x10));
153 cx22700_writereg (state
, 0x09, cx22700_readreg (state
, 0x09 & ~0x10));
155 val
= qam_tab
[p
->modulation
- QPSK
];
156 val
|= p
->hierarchy
- HIERARCHY_NONE
;
158 cx22700_writereg (state
, 0x04, val
);
160 if (p
->code_rate_HP
- FEC_1_2
>= sizeof(fec_tab
) ||
161 p
->code_rate_LP
- FEC_1_2
>= sizeof(fec_tab
))
163 val
= fec_tab
[p
->code_rate_HP
- FEC_1_2
] << 3;
164 val
|= fec_tab
[p
->code_rate_LP
- FEC_1_2
];
166 cx22700_writereg (state
, 0x05, val
);
168 val
= (p
->guard_interval
- GUARD_INTERVAL_1_32
) << 2;
169 val
|= p
->transmission_mode
- TRANSMISSION_MODE_2K
;
171 cx22700_writereg (state
, 0x06, val
);
173 cx22700_writereg (state
, 0x08, 0x04 | 0x02); /* use user tps parameters */
174 cx22700_writereg (state
, 0x08, 0x04); /* restart acquisition */
179 static int cx22700_get_tps(struct cx22700_state
*state
,
180 struct dtv_frontend_properties
*p
)
182 static const enum fe_modulation qam_tab
[3] = { QPSK
, QAM_16
, QAM_64
};
183 static const enum fe_code_rate fec_tab
[5] = {
184 FEC_1_2
, FEC_2_3
, FEC_3_4
, FEC_5_6
, FEC_7_8
188 dprintk ("%s\n", __func__
);
190 if (!(cx22700_readreg(state
, 0x07) & 0x20)) /* tps valid? */
193 val
= cx22700_readreg (state
, 0x01);
196 p
->hierarchy
= HIERARCHY_AUTO
;
198 p
->hierarchy
= HIERARCHY_NONE
+ (val
& 0x7);
200 if (((val
>> 3) & 0x3) > 2)
201 p
->modulation
= QAM_AUTO
;
203 p
->modulation
= qam_tab
[(val
>> 3) & 0x3];
205 val
= cx22700_readreg (state
, 0x02);
207 if (((val
>> 3) & 0x07) > 4)
208 p
->code_rate_HP
= FEC_AUTO
;
210 p
->code_rate_HP
= fec_tab
[(val
>> 3) & 0x07];
212 if ((val
& 0x07) > 4)
213 p
->code_rate_LP
= FEC_AUTO
;
215 p
->code_rate_LP
= fec_tab
[val
& 0x07];
217 val
= cx22700_readreg (state
, 0x03);
219 p
->guard_interval
= GUARD_INTERVAL_1_32
+ ((val
>> 6) & 0x3);
220 p
->transmission_mode
= TRANSMISSION_MODE_2K
+ ((val
>> 5) & 0x1);
225 static int cx22700_init (struct dvb_frontend
* fe
)
227 { struct cx22700_state
* state
= fe
->demodulator_priv
;
230 dprintk("cx22700_init: init chip\n");
232 cx22700_writereg (state
, 0x00, 0x02); /* soft reset */
233 cx22700_writereg (state
, 0x00, 0x00);
237 for (i
=0; i
<sizeof(init_tab
); i
+=2)
238 cx22700_writereg (state
, init_tab
[i
], init_tab
[i
+1]);
240 cx22700_writereg (state
, 0x00, 0x01);
245 static int cx22700_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
247 struct cx22700_state
* state
= fe
->demodulator_priv
;
249 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
250 | (cx22700_readreg (state
, 0x0e) << 1);
251 u8 sync
= cx22700_readreg (state
, 0x07);
256 *status
|= FE_HAS_SIGNAL
;
259 *status
|= FE_HAS_CARRIER
;
262 *status
|= FE_HAS_VITERBI
;
265 *status
|= FE_HAS_SYNC
;
268 *status
|= FE_HAS_LOCK
;
273 static int cx22700_read_ber(struct dvb_frontend
* fe
, u32
* ber
)
275 struct cx22700_state
* state
= fe
->demodulator_priv
;
277 *ber
= cx22700_readreg (state
, 0x0c) & 0x7f;
278 cx22700_writereg (state
, 0x0c, 0x00);
283 static int cx22700_read_signal_strength(struct dvb_frontend
* fe
, u16
* signal_strength
)
285 struct cx22700_state
* state
= fe
->demodulator_priv
;
287 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
288 | (cx22700_readreg (state
, 0x0e) << 1);
289 *signal_strength
= ~rs_ber
;
294 static int cx22700_read_snr(struct dvb_frontend
* fe
, u16
* snr
)
296 struct cx22700_state
* state
= fe
->demodulator_priv
;
298 u16 rs_ber
= (cx22700_readreg (state
, 0x0d) << 9)
299 | (cx22700_readreg (state
, 0x0e) << 1);
305 static int cx22700_read_ucblocks(struct dvb_frontend
* fe
, u32
* ucblocks
)
307 struct cx22700_state
* state
= fe
->demodulator_priv
;
309 *ucblocks
= cx22700_readreg (state
, 0x0f);
310 cx22700_writereg (state
, 0x0f, 0x00);
315 static int cx22700_set_frontend(struct dvb_frontend
*fe
)
317 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
318 struct cx22700_state
* state
= fe
->demodulator_priv
;
320 cx22700_writereg (state
, 0x00, 0x02); /* XXX CHECKME: soft reset*/
321 cx22700_writereg (state
, 0x00, 0x00);
323 if (fe
->ops
.tuner_ops
.set_params
) {
324 fe
->ops
.tuner_ops
.set_params(fe
);
325 if (fe
->ops
.i2c_gate_ctrl
) fe
->ops
.i2c_gate_ctrl(fe
, 0);
328 cx22700_set_inversion(state
, c
->inversion
);
329 cx22700_set_tps(state
, c
);
330 cx22700_writereg (state
, 0x37, 0x01); /* PAL loop filter off */
331 cx22700_writereg (state
, 0x00, 0x01); /* restart acquire */
336 static int cx22700_get_frontend(struct dvb_frontend
*fe
,
337 struct dtv_frontend_properties
*c
)
339 struct cx22700_state
* state
= fe
->demodulator_priv
;
340 u8 reg09
= cx22700_readreg (state
, 0x09);
342 c
->inversion
= reg09
& 0x1 ? INVERSION_ON
: INVERSION_OFF
;
343 return cx22700_get_tps(state
, c
);
346 static int cx22700_i2c_gate_ctrl(struct dvb_frontend
* fe
, int enable
)
348 struct cx22700_state
* state
= fe
->demodulator_priv
;
351 return cx22700_writereg(state
, 0x0a, 0x00);
353 return cx22700_writereg(state
, 0x0a, 0x01);
357 static int cx22700_get_tune_settings(struct dvb_frontend
* fe
, struct dvb_frontend_tune_settings
* fesettings
)
359 fesettings
->min_delay_ms
= 150;
360 fesettings
->step_size
= 166667;
361 fesettings
->max_drift
= 166667*2;
365 static void cx22700_release(struct dvb_frontend
* fe
)
367 struct cx22700_state
* state
= fe
->demodulator_priv
;
371 static const struct dvb_frontend_ops cx22700_ops
;
373 struct dvb_frontend
* cx22700_attach(const struct cx22700_config
* config
,
374 struct i2c_adapter
* i2c
)
376 struct cx22700_state
* state
= NULL
;
378 /* allocate memory for the internal state */
379 state
= kzalloc(sizeof(struct cx22700_state
), GFP_KERNEL
);
380 if (state
== NULL
) goto error
;
382 /* setup the state */
383 state
->config
= config
;
386 /* check if the demod is there */
387 if (cx22700_readreg(state
, 0x07) < 0) goto error
;
389 /* create dvb_frontend */
390 memcpy(&state
->frontend
.ops
, &cx22700_ops
, sizeof(struct dvb_frontend_ops
));
391 state
->frontend
.demodulator_priv
= state
;
392 return &state
->frontend
;
399 static const struct dvb_frontend_ops cx22700_ops
= {
400 .delsys
= { SYS_DVBT
},
402 .name
= "Conexant CX22700 DVB-T",
403 .frequency_min_hz
= 470 * MHz
,
404 .frequency_max_hz
= 860 * MHz
,
405 .frequency_stepsize_hz
= 166667,
406 .caps
= FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
407 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
408 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
412 .release
= cx22700_release
,
414 .init
= cx22700_init
,
415 .i2c_gate_ctrl
= cx22700_i2c_gate_ctrl
,
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
);