1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3     Conexant cx22700 DVB OFDM demodulator driver
4 
5     Copyright (C) 2001-2002 Convergence Integrated Media GmbH
6 	Holger Waechtler <holger@convergence.de>
7 
8 
9 */
10 
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>
17 #include "cx22700.h"
18 
19 
20 struct cx22700_state {
21 
22 	struct i2c_adapter* i2c;
23 
24 	const struct cx22700_config* config;
25 
26 	struct dvb_frontend frontend;
27 };
28 
29 
30 static int debug;
31 #define dprintk(args...) \
32 	do { \
33 		if (debug) printk(KERN_DEBUG "cx22700: " args); \
34 	} while (0)
35 
36 static u8 init_tab [] = {
37 	0x04, 0x10,
38 	0x05, 0x09,
39 	0x06, 0x00,
40 	0x08, 0x04,
41 	0x09, 0x00,
42 	0x0a, 0x01,
43 	0x15, 0x40,
44 	0x16, 0x10,
45 	0x17, 0x87,
46 	0x18, 0x17,
47 	0x1a, 0x10,
48 	0x25, 0x04,
49 	0x2e, 0x00,
50 	0x39, 0x00,
51 	0x3a, 0x04,
52 	0x45, 0x08,
53 	0x46, 0x02,
54 	0x47, 0x05,
55 };
56 
57 
cx22700_writereg(struct cx22700_state * state,u8 reg,u8 data)58 static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
59 {
60 	int ret;
61 	u8 buf [] = { reg, data };
62 	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
63 
64 	dprintk ("%s\n", __func__);
65 
66 	ret = i2c_transfer (state->i2c, &msg, 1);
67 
68 	if (ret != 1)
69 		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
70 			__func__, reg, data, ret);
71 
72 	return (ret != 1) ? -1 : 0;
73 }
74 
cx22700_readreg(struct cx22700_state * state,u8 reg)75 static int cx22700_readreg (struct cx22700_state* state, u8 reg)
76 {
77 	int ret;
78 	u8 b0 [] = { reg };
79 	u8 b1 [] = { 0 };
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 } };
82 
83 	dprintk ("%s\n", __func__);
84 
85 	ret = i2c_transfer (state->i2c, msg, 2);
86 
87 	if (ret != 2) return -EIO;
88 
89 	return b1[0];
90 }
91 
cx22700_set_inversion(struct cx22700_state * state,int inversion)92 static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
93 {
94 	u8 val;
95 
96 	dprintk ("%s\n", __func__);
97 
98 	switch (inversion) {
99 	case INVERSION_AUTO:
100 		return -EOPNOTSUPP;
101 	case INVERSION_ON:
102 		val = cx22700_readreg (state, 0x09);
103 		return cx22700_writereg (state, 0x09, val | 0x01);
104 	case INVERSION_OFF:
105 		val = cx22700_readreg (state, 0x09);
106 		return cx22700_writereg (state, 0x09, val & 0xfe);
107 	default:
108 		return -EINVAL;
109 	}
110 }
111 
cx22700_set_tps(struct cx22700_state * state,struct dtv_frontend_properties * p)112 static int cx22700_set_tps(struct cx22700_state *state,
113 			   struct dtv_frontend_properties *p)
114 {
115 	static const u8 qam_tab [4] = { 0, 1, 0, 2 };
116 	static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
117 	u8 val;
118 
119 	dprintk ("%s\n", __func__);
120 
121 	if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
122 		return -EINVAL;
123 
124 	if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
125 		return -EINVAL;
126 
127 	if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
128 		return -EINVAL;
129 
130 	if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
131 	    p->guard_interval > GUARD_INTERVAL_1_4)
132 		return -EINVAL;
133 
134 	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
135 	    p->transmission_mode != TRANSMISSION_MODE_8K)
136 		return -EINVAL;
137 
138 	if (p->modulation != QPSK &&
139 	    p->modulation != QAM_16 &&
140 	    p->modulation != QAM_64)
141 		return -EINVAL;
142 
143 	if ((int)p->hierarchy < HIERARCHY_NONE ||
144 	    p->hierarchy > HIERARCHY_4)
145 		return -EINVAL;
146 
147 	if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000)
148 		return -EINVAL;
149 
150 	if (p->bandwidth_hz == 7000000)
151 		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
152 	else
153 		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
154 
155 	val = qam_tab[p->modulation - QPSK];
156 	val |= p->hierarchy - HIERARCHY_NONE;
157 
158 	cx22700_writereg (state, 0x04, val);
159 
160 	if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) ||
161 	    p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab))
162 		return -EINVAL;
163 	val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
164 	val |= fec_tab[p->code_rate_LP - FEC_1_2];
165 
166 	cx22700_writereg (state, 0x05, val);
167 
168 	val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
169 	val |= p->transmission_mode - TRANSMISSION_MODE_2K;
170 
171 	cx22700_writereg (state, 0x06, val);
172 
173 	cx22700_writereg (state, 0x08, 0x04 | 0x02);  /* use user tps parameters */
174 	cx22700_writereg (state, 0x08, 0x04);         /* restart acquisition */
175 
176 	return 0;
177 }
178 
cx22700_get_tps(struct cx22700_state * state,struct dtv_frontend_properties * p)179 static int cx22700_get_tps(struct cx22700_state *state,
180 			   struct dtv_frontend_properties *p)
181 {
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
185 	};
186 	u8 val;
187 
188 	dprintk ("%s\n", __func__);
189 
190 	if (!(cx22700_readreg(state, 0x07) & 0x20))  /*  tps valid? */
191 		return -EAGAIN;
192 
193 	val = cx22700_readreg (state, 0x01);
194 
195 	if ((val & 0x7) > 4)
196 		p->hierarchy = HIERARCHY_AUTO;
197 	else
198 		p->hierarchy = HIERARCHY_NONE + (val & 0x7);
199 
200 	if (((val >> 3) & 0x3) > 2)
201 		p->modulation = QAM_AUTO;
202 	else
203 		p->modulation = qam_tab[(val >> 3) & 0x3];
204 
205 	val = cx22700_readreg (state, 0x02);
206 
207 	if (((val >> 3) & 0x07) > 4)
208 		p->code_rate_HP = FEC_AUTO;
209 	else
210 		p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
211 
212 	if ((val & 0x07) > 4)
213 		p->code_rate_LP = FEC_AUTO;
214 	else
215 		p->code_rate_LP = fec_tab[val & 0x07];
216 
217 	val = cx22700_readreg (state, 0x03);
218 
219 	p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
220 	p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
221 
222 	return 0;
223 }
224 
cx22700_init(struct dvb_frontend * fe)225 static int cx22700_init (struct dvb_frontend* fe)
226 
227 {	struct cx22700_state* state = fe->demodulator_priv;
228 	int i;
229 
230 	dprintk("cx22700_init: init chip\n");
231 
232 	cx22700_writereg (state, 0x00, 0x02);   /*  soft reset */
233 	cx22700_writereg (state, 0x00, 0x00);
234 
235 	msleep(10);
236 
237 	for (i=0; i<sizeof(init_tab); i+=2)
238 		cx22700_writereg (state, init_tab[i], init_tab[i+1]);
239 
240 	cx22700_writereg (state, 0x00, 0x01);
241 
242 	return 0;
243 }
244 
cx22700_read_status(struct dvb_frontend * fe,enum fe_status * status)245 static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status)
246 {
247 	struct cx22700_state* state = fe->demodulator_priv;
248 
249 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
250 		   | (cx22700_readreg (state, 0x0e) << 1);
251 	u8 sync = cx22700_readreg (state, 0x07);
252 
253 	*status = 0;
254 
255 	if (rs_ber < 0xff00)
256 		*status |= FE_HAS_SIGNAL;
257 
258 	if (sync & 0x20)
259 		*status |= FE_HAS_CARRIER;
260 
261 	if (sync & 0x10)
262 		*status |= FE_HAS_VITERBI;
263 
264 	if (sync & 0x10)
265 		*status |= FE_HAS_SYNC;
266 
267 	if (*status == 0x0f)
268 		*status |= FE_HAS_LOCK;
269 
270 	return 0;
271 }
272 
cx22700_read_ber(struct dvb_frontend * fe,u32 * ber)273 static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
274 {
275 	struct cx22700_state* state = fe->demodulator_priv;
276 
277 	*ber = cx22700_readreg (state, 0x0c) & 0x7f;
278 	cx22700_writereg (state, 0x0c, 0x00);
279 
280 	return 0;
281 }
282 
cx22700_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)283 static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
284 {
285 	struct cx22700_state* state = fe->demodulator_priv;
286 
287 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
288 		   | (cx22700_readreg (state, 0x0e) << 1);
289 	*signal_strength = ~rs_ber;
290 
291 	return 0;
292 }
293 
cx22700_read_snr(struct dvb_frontend * fe,u16 * snr)294 static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
295 {
296 	struct cx22700_state* state = fe->demodulator_priv;
297 
298 	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
299 		   | (cx22700_readreg (state, 0x0e) << 1);
300 	*snr = ~rs_ber;
301 
302 	return 0;
303 }
304 
cx22700_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)305 static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
306 {
307 	struct cx22700_state* state = fe->demodulator_priv;
308 
309 	*ucblocks = cx22700_readreg (state, 0x0f);
310 	cx22700_writereg (state, 0x0f, 0x00);
311 
312 	return 0;
313 }
314 
cx22700_set_frontend(struct dvb_frontend * fe)315 static int cx22700_set_frontend(struct dvb_frontend *fe)
316 {
317 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
318 	struct cx22700_state* state = fe->demodulator_priv;
319 
320 	cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
321 	cx22700_writereg (state, 0x00, 0x00);
322 
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);
326 	}
327 
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 */
332 
333 	return 0;
334 }
335 
cx22700_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * c)336 static int cx22700_get_frontend(struct dvb_frontend *fe,
337 				struct dtv_frontend_properties *c)
338 {
339 	struct cx22700_state* state = fe->demodulator_priv;
340 	u8 reg09 = cx22700_readreg (state, 0x09);
341 
342 	c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
343 	return cx22700_get_tps(state, c);
344 }
345 
cx22700_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)346 static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
347 {
348 	struct cx22700_state* state = fe->demodulator_priv;
349 
350 	if (enable) {
351 		return cx22700_writereg(state, 0x0a, 0x00);
352 	} else {
353 		return cx22700_writereg(state, 0x0a, 0x01);
354 	}
355 }
356 
cx22700_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * fesettings)357 static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
358 {
359 	fesettings->min_delay_ms = 150;
360 	fesettings->step_size = 166667;
361 	fesettings->max_drift = 166667*2;
362 	return 0;
363 }
364 
cx22700_release(struct dvb_frontend * fe)365 static void cx22700_release(struct dvb_frontend* fe)
366 {
367 	struct cx22700_state* state = fe->demodulator_priv;
368 	kfree(state);
369 }
370 
371 static const struct dvb_frontend_ops cx22700_ops;
372 
cx22700_attach(const struct cx22700_config * config,struct i2c_adapter * i2c)373 struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
374 				    struct i2c_adapter* i2c)
375 {
376 	struct cx22700_state* state = NULL;
377 
378 	/* allocate memory for the internal state */
379 	state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL);
380 	if (state == NULL) goto error;
381 
382 	/* setup the state */
383 	state->config = config;
384 	state->i2c = i2c;
385 
386 	/* check if the demod is there */
387 	if (cx22700_readreg(state, 0x07) < 0) goto error;
388 
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;
393 
394 error:
395 	kfree(state);
396 	return NULL;
397 }
398 
399 static const struct dvb_frontend_ops cx22700_ops = {
400 	.delsys = { SYS_DVBT },
401 	.info = {
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 |
409 		      FE_CAN_RECOVER
410 	},
411 
412 	.release = cx22700_release,
413 
414 	.init = cx22700_init,
415 	.i2c_gate_ctrl = cx22700_i2c_gate_ctrl,
416 
417 	.set_frontend = cx22700_set_frontend,
418 	.get_frontend = cx22700_get_frontend,
419 	.get_tune_settings = cx22700_get_tune_settings,
420 
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,
426 };
427 
428 module_param(debug, int, 0644);
429 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
430 
431 MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
432 MODULE_AUTHOR("Holger Waechtler");
433 MODULE_LICENSE("GPL");
434 
435 EXPORT_SYMBOL_GPL(cx22700_attach);
436