PageRenderTime 97ms CodeModel.GetById 27ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/media/dvb/frontends/cx22702.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 638 lines | 501 code | 88 blank | 49 comment | 43 complexity | baf2177d6434864d2474e585cb86a896 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/*
  2    Conexant 22702 DVB OFDM demodulator driver
  3
  4    based on:
  5	Alps TDMB7 DVB OFDM demodulator driver
  6
  7    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
  8	  Holger Waechtler <holger@convergence.de>
  9
 10    Copyright (C) 2004 Steven Toth <stoth@linuxtv.org>
 11
 12    This program is free software; you can redistribute it and/or modify
 13    it under the terms of the GNU General Public License as published by
 14    the Free Software Foundation; either version 2 of the License, or
 15    (at your option) any later version.
 16
 17    This program is distributed in the hope that it will be useful,
 18    but WITHOUT ANY WARRANTY; without even the implied warranty of
 19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20    GNU General Public License for more details.
 21
 22    You should have received a copy of the GNU General Public License
 23    along with this program; if not, write to the Free Software
 24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 25
 26*/
 27
 28#include <linux/kernel.h>
 29#include <linux/init.h>
 30#include <linux/module.h>
 31#include <linux/string.h>
 32#include <linux/slab.h>
 33#include <linux/delay.h>
 34#include "dvb_frontend.h"
 35#include "cx22702.h"
 36
 37struct cx22702_state {
 38
 39	struct i2c_adapter *i2c;
 40
 41	/* configuration settings */
 42	const struct cx22702_config *config;
 43
 44	struct dvb_frontend frontend;
 45
 46	/* previous uncorrected block counter */
 47	u8 prevUCBlocks;
 48};
 49
 50static int debug;
 51module_param(debug, int, 0644);
 52MODULE_PARM_DESC(debug, "Enable verbose debug messages");
 53
 54#define dprintk	if (debug) printk
 55
 56/* Register values to initialise the demod */
 57static const u8 init_tab[] = {
 58	0x00, 0x00, /* Stop acquisition */
 59	0x0B, 0x06,
 60	0x09, 0x01,
 61	0x0D, 0x41,
 62	0x16, 0x32,
 63	0x20, 0x0A,
 64	0x21, 0x17,
 65	0x24, 0x3e,
 66	0x26, 0xff,
 67	0x27, 0x10,
 68	0x28, 0x00,
 69	0x29, 0x00,
 70	0x2a, 0x10,
 71	0x2b, 0x00,
 72	0x2c, 0x10,
 73	0x2d, 0x00,
 74	0x48, 0xd4,
 75	0x49, 0x56,
 76	0x6b, 0x1e,
 77	0xc8, 0x02,
 78	0xf9, 0x00,
 79	0xfa, 0x00,
 80	0xfb, 0x00,
 81	0xfc, 0x00,
 82	0xfd, 0x00,
 83};
 84
 85static int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data)
 86{
 87	int ret;
 88	u8 buf[] = { reg, data };
 89	struct i2c_msg msg = {
 90		.addr = state->config->demod_address, .flags = 0,
 91			.buf = buf, .len = 2 };
 92
 93	ret = i2c_transfer(state->i2c, &msg, 1);
 94
 95	if (unlikely(ret != 1)) {
 96		printk(KERN_ERR
 97			"%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
 98			__func__, reg, data, ret);
 99		return -1;
100	}
101
102	return 0;
103}
104
105static u8 cx22702_readreg(struct cx22702_state *state, u8 reg)
106{
107	int ret;
108	u8 data;
109
110	struct i2c_msg msg[] = {
111		{ .addr = state->config->demod_address, .flags = 0,
112			.buf = &reg, .len = 1 },
113		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
114			.buf = &data, .len = 1 } };
115
116	ret = i2c_transfer(state->i2c, msg, 2);
117
118	if (unlikely(ret != 2)) {
119		printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n",
120			__func__, reg, ret);
121		return 0;
122	}
123
124	return data;
125}
126
127static int cx22702_set_inversion(struct cx22702_state *state, int inversion)
128{
129	u8 val;
130
131	val = cx22702_readreg(state, 0x0C);
132	switch (inversion) {
133	case INVERSION_AUTO:
134		return -EOPNOTSUPP;
135	case INVERSION_ON:
136		val |= 0x01;
137		break;
138	case INVERSION_OFF:
139		val &= 0xfe;
140		break;
141	default:
142		return -EINVAL;
143	}
144	return cx22702_writereg(state, 0x0C, val);
145}
146
147/* Retrieve the demod settings */
148static int cx22702_get_tps(struct cx22702_state *state,
149	struct dvb_ofdm_parameters *p)
150{
151	u8 val;
152
153	/* Make sure the TPS regs are valid */
154	if (!(cx22702_readreg(state, 0x0A) & 0x20))
155		return -EAGAIN;
156
157	val = cx22702_readreg(state, 0x01);
158	switch ((val & 0x18) >> 3) {
159	case 0:
160		p->constellation = QPSK;
161		break;
162	case 1:
163		p->constellation = QAM_16;
164		break;
165	case 2:
166		p->constellation = QAM_64;
167		break;
168	}
169	switch (val & 0x07) {
170	case 0:
171		p->hierarchy_information = HIERARCHY_NONE;
172		break;
173	case 1:
174		p->hierarchy_information = HIERARCHY_1;
175		break;
176	case 2:
177		p->hierarchy_information = HIERARCHY_2;
178		break;
179	case 3:
180		p->hierarchy_information = HIERARCHY_4;
181		break;
182	}
183
184
185	val = cx22702_readreg(state, 0x02);
186	switch ((val & 0x38) >> 3) {
187	case 0:
188		p->code_rate_HP = FEC_1_2;
189		break;
190	case 1:
191		p->code_rate_HP = FEC_2_3;
192		break;
193	case 2:
194		p->code_rate_HP = FEC_3_4;
195		break;
196	case 3:
197		p->code_rate_HP = FEC_5_6;
198		break;
199	case 4:
200		p->code_rate_HP = FEC_7_8;
201		break;
202	}
203	switch (val & 0x07) {
204	case 0:
205		p->code_rate_LP = FEC_1_2;
206		break;
207	case 1:
208		p->code_rate_LP = FEC_2_3;
209		break;
210	case 2:
211		p->code_rate_LP = FEC_3_4;
212		break;
213	case 3:
214		p->code_rate_LP = FEC_5_6;
215		break;
216	case 4:
217		p->code_rate_LP = FEC_7_8;
218		break;
219	}
220
221	val = cx22702_readreg(state, 0x03);
222	switch ((val & 0x0c) >> 2) {
223	case 0:
224		p->guard_interval = GUARD_INTERVAL_1_32;
225		break;
226	case 1:
227		p->guard_interval = GUARD_INTERVAL_1_16;
228		break;
229	case 2:
230		p->guard_interval = GUARD_INTERVAL_1_8;
231		break;
232	case 3:
233		p->guard_interval = GUARD_INTERVAL_1_4;
234		break;
235	}
236	switch (val & 0x03) {
237	case 0:
238		p->transmission_mode = TRANSMISSION_MODE_2K;
239		break;
240	case 1:
241		p->transmission_mode = TRANSMISSION_MODE_8K;
242		break;
243	}
244
245	return 0;
246}
247
248static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
249{
250	struct cx22702_state *state = fe->demodulator_priv;
251	u8 val;
252
253	dprintk("%s(%d)\n", __func__, enable);
254	val = cx22702_readreg(state, 0x0D);
255	if (enable)
256		val &= 0xfe;
257	else
258		val |= 0x01;
259	return cx22702_writereg(state, 0x0D, val);
260}
261
262/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
263static int cx22702_set_tps(struct dvb_frontend *fe,
264	struct dvb_frontend_parameters *p)
265{
266	u8 val;
267	struct cx22702_state *state = fe->demodulator_priv;
268
269	if (fe->ops.tuner_ops.set_params) {
270		fe->ops.tuner_ops.set_params(fe, p);
271		if (fe->ops.i2c_gate_ctrl)
272			fe->ops.i2c_gate_ctrl(fe, 0);
273	}
274
275	/* set inversion */
276	cx22702_set_inversion(state, p->inversion);
277
278	/* set bandwidth */
279	val = cx22702_readreg(state, 0x0C) & 0xcf;
280	switch (p->u.ofdm.bandwidth) {
281	case BANDWIDTH_6_MHZ:
282		val |= 0x20;
283		break;
284	case BANDWIDTH_7_MHZ:
285		val |= 0x10;
286		break;
287	case BANDWIDTH_8_MHZ:
288		break;
289	default:
290		dprintk("%s: invalid bandwidth\n", __func__);
291		return -EINVAL;
292	}
293	cx22702_writereg(state, 0x0C, val);
294
295	p->u.ofdm.code_rate_LP = FEC_AUTO; /* temp hack as manual not working */
296
297	/* use auto configuration? */
298	if ((p->u.ofdm.hierarchy_information == HIERARCHY_AUTO) ||
299	   (p->u.ofdm.constellation == QAM_AUTO) ||
300	   (p->u.ofdm.code_rate_HP == FEC_AUTO) ||
301	   (p->u.ofdm.code_rate_LP == FEC_AUTO) ||
302	   (p->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO) ||
303	   (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO)) {
304
305		/* TPS Source - use hardware driven values */
306		cx22702_writereg(state, 0x06, 0x10);
307		cx22702_writereg(state, 0x07, 0x9);
308		cx22702_writereg(state, 0x08, 0xC1);
309		cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B)
310			& 0xfc);
311		cx22702_writereg(state, 0x0C,
312			(cx22702_readreg(state, 0x0C) & 0xBF) | 0x40);
313		cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */
314		dprintk("%s: Autodetecting\n", __func__);
315		return 0;
316	}
317
318	/* manually programmed values */
319	switch (p->u.ofdm.constellation) {		/* mask 0x18 */
320	case QPSK:
321		val = 0x00;
322		break;
323	case QAM_16:
324		val = 0x08;
325		break;
326	case QAM_64:
327		val = 0x10;
328		break;
329	default:
330		dprintk("%s: invalid constellation\n", __func__);
331		return -EINVAL;
332	}
333	switch (p->u.ofdm.hierarchy_information) {	/* mask 0x07 */
334	case HIERARCHY_NONE:
335		break;
336	case HIERARCHY_1:
337		val |= 0x01;
338		break;
339	case HIERARCHY_2:
340		val |= 0x02;
341		break;
342	case HIERARCHY_4:
343		val |= 0x03;
344		break;
345	default:
346		dprintk("%s: invalid hierarchy\n", __func__);
347		return -EINVAL;
348	}
349	cx22702_writereg(state, 0x06, val);
350
351	switch (p->u.ofdm.code_rate_HP) {		/* mask 0x38 */
352	case FEC_NONE:
353	case FEC_1_2:
354		val = 0x00;
355		break;
356	case FEC_2_3:
357		val = 0x08;
358		break;
359	case FEC_3_4:
360		val = 0x10;
361		break;
362	case FEC_5_6:
363		val = 0x18;
364		break;
365	case FEC_7_8:
366		val = 0x20;
367		break;
368	default:
369		dprintk("%s: invalid code_rate_HP\n", __func__);
370		return -EINVAL;
371	}
372	switch (p->u.ofdm.code_rate_LP) {		/* mask 0x07 */
373	case FEC_NONE:
374	case FEC_1_2:
375		break;
376	case FEC_2_3:
377		val |= 0x01;
378		break;
379	case FEC_3_4:
380		val |= 0x02;
381		break;
382	case FEC_5_6:
383		val |= 0x03;
384		break;
385	case FEC_7_8:
386		val |= 0x04;
387		break;
388	default:
389		dprintk("%s: invalid code_rate_LP\n", __func__);
390		return -EINVAL;
391	}
392	cx22702_writereg(state, 0x07, val);
393
394	switch (p->u.ofdm.guard_interval) {		/* mask 0x0c */
395	case GUARD_INTERVAL_1_32:
396		val = 0x00;
397		break;
398	case GUARD_INTERVAL_1_16:
399		val = 0x04;
400		break;
401	case GUARD_INTERVAL_1_8:
402		val = 0x08;
403		break;
404	case GUARD_INTERVAL_1_4:
405		val = 0x0c;
406		break;
407	default:
408		dprintk("%s: invalid guard_interval\n", __func__);
409		return -EINVAL;
410	}
411	switch (p->u.ofdm.transmission_mode) {		/* mask 0x03 */
412	case TRANSMISSION_MODE_2K:
413		break;
414	case TRANSMISSION_MODE_8K:
415		val |= 0x1;
416		break;
417	default:
418		dprintk("%s: invalid transmission_mode\n", __func__);
419		return -EINVAL;
420	}
421	cx22702_writereg(state, 0x08, val);
422	cx22702_writereg(state, 0x0B,
423		(cx22702_readreg(state, 0x0B) & 0xfc) | 0x02);
424	cx22702_writereg(state, 0x0C,
425		(cx22702_readreg(state, 0x0C) & 0xBF) | 0x40);
426
427	/* Begin channel acquisition */
428	cx22702_writereg(state, 0x00, 0x01);
429
430	return 0;
431}
432
433/* Reset the demod hardware and reset all of the configuration registers
434   to a default state. */
435static int cx22702_init(struct dvb_frontend *fe)
436{
437	int i;
438	struct cx22702_state *state = fe->demodulator_priv;
439
440	cx22702_writereg(state, 0x00, 0x02);
441
442	msleep(10);
443
444	for (i = 0; i < ARRAY_SIZE(init_tab); i += 2)
445		cx22702_writereg(state, init_tab[i], init_tab[i + 1]);
446
447	cx22702_writereg(state, 0xf8, (state->config->output_mode << 1)
448		& 0x02);
449
450	cx22702_i2c_gate_ctrl(fe, 0);
451
452	return 0;
453}
454
455static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status)
456{
457	struct cx22702_state *state = fe->demodulator_priv;
458	u8 reg0A;
459	u8 reg23;
460
461	*status = 0;
462
463	reg0A = cx22702_readreg(state, 0x0A);
464	reg23 = cx22702_readreg(state, 0x23);
465
466	dprintk("%s: status demod=0x%02x agc=0x%02x\n"
467		, __func__, reg0A, reg23);
468
469	if (reg0A & 0x10) {
470		*status |= FE_HAS_LOCK;
471		*status |= FE_HAS_VITERBI;
472		*status |= FE_HAS_SYNC;
473	}
474
475	if (reg0A & 0x20)
476		*status |= FE_HAS_CARRIER;
477
478	if (reg23 < 0xf0)
479		*status |= FE_HAS_SIGNAL;
480
481	return 0;
482}
483
484static int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber)
485{
486	struct cx22702_state *state = fe->demodulator_priv;
487
488	if (cx22702_readreg(state, 0xE4) & 0x02) {
489		/* Realtime statistics */
490		*ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
491			| (cx22702_readreg(state, 0xDF) & 0x7F);
492	} else {
493		/* Averagtine statistics */
494		*ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
495			| cx22702_readreg(state, 0xDF);
496	}
497
498	return 0;
499}
500
501static int cx22702_read_signal_strength(struct dvb_frontend *fe,
502	u16 *signal_strength)
503{
504	struct cx22702_state *state = fe->demodulator_priv;
505
506	u16 rs_ber;
507	rs_ber = cx22702_readreg(state, 0x23);
508	*signal_strength = (rs_ber << 8) | rs_ber;
509
510	return 0;
511}
512
513static int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr)
514{
515	struct cx22702_state *state = fe->demodulator_priv;
516
517	u16 rs_ber;
518	if (cx22702_readreg(state, 0xE4) & 0x02) {
519		/* Realtime statistics */
520		rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
521			| (cx22702_readreg(state, 0xDF) & 0x7F);
522	} else {
523		/* Averagine statistics */
524		rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8
525			| cx22702_readreg(state, 0xDF);
526	}
527	*snr = ~rs_ber;
528
529	return 0;
530}
531
532static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
533{
534	struct cx22702_state *state = fe->demodulator_priv;
535
536	u8 _ucblocks;
537
538	/* RS Uncorrectable Packet Count then reset */
539	_ucblocks = cx22702_readreg(state, 0xE3);
540	if (state->prevUCBlocks < _ucblocks)
541		*ucblocks = (_ucblocks - state->prevUCBlocks);
542	else
543		*ucblocks = state->prevUCBlocks - _ucblocks;
544	state->prevUCBlocks = _ucblocks;
545
546	return 0;
547}
548
549static int cx22702_get_frontend(struct dvb_frontend *fe,
550	struct dvb_frontend_parameters *p)
551{
552	struct cx22702_state *state = fe->demodulator_priv;
553
554	u8 reg0C = cx22702_readreg(state, 0x0C);
555
556	p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
557	return cx22702_get_tps(state, &p->u.ofdm);
558}
559
560static int cx22702_get_tune_settings(struct dvb_frontend *fe,
561	struct dvb_frontend_tune_settings *tune)
562{
563	tune->min_delay_ms = 1000;
564	return 0;
565}
566
567static void cx22702_release(struct dvb_frontend *fe)
568{
569	struct cx22702_state *state = fe->demodulator_priv;
570	kfree(state);
571}
572
573static const struct dvb_frontend_ops cx22702_ops;
574
575struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
576	struct i2c_adapter *i2c)
577{
578	struct cx22702_state *state = NULL;
579
580	/* allocate memory for the internal state */
581	state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL);
582	if (state == NULL)
583		goto error;
584
585	/* setup the state */
586	state->config = config;
587	state->i2c = i2c;
588
589	/* check if the demod is there */
590	if (cx22702_readreg(state, 0x1f) != 0x3)
591		goto error;
592
593	/* create dvb_frontend */
594	memcpy(&state->frontend.ops, &cx22702_ops,
595		sizeof(struct dvb_frontend_ops));
596	state->frontend.demodulator_priv = state;
597	return &state->frontend;
598
599error:
600	kfree(state);
601	return NULL;
602}
603EXPORT_SYMBOL(cx22702_attach);
604
605static const struct dvb_frontend_ops cx22702_ops = {
606
607	.info = {
608		.name			= "Conexant CX22702 DVB-T",
609		.type			= FE_OFDM,
610		.frequency_min		= 177000000,
611		.frequency_max		= 858000000,
612		.frequency_stepsize	= 166666,
613		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
614		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
615		FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
616		FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
617		FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
618	},
619
620	.release = cx22702_release,
621
622	.init = cx22702_init,
623	.i2c_gate_ctrl = cx22702_i2c_gate_ctrl,
624
625	.set_frontend = cx22702_set_tps,
626	.get_frontend = cx22702_get_frontend,
627	.get_tune_settings = cx22702_get_tune_settings,
628
629	.read_status = cx22702_read_status,
630	.read_ber = cx22702_read_ber,
631	.read_signal_strength = cx22702_read_signal_strength,
632	.read_snr = cx22702_read_snr,
633	.read_ucblocks = cx22702_read_ucblocks,
634};
635
636MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
637MODULE_AUTHOR("Steven Toth");
638MODULE_LICENSE("GPL");