xref: /openbmc/linux/drivers/media/dvb-frontends/l64781.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29a0bf528SMauro Carvalho Chehab /*
39a0bf528SMauro Carvalho Chehab     driver for LSI L64781 COFDM demodulator
49a0bf528SMauro Carvalho Chehab 
59a0bf528SMauro Carvalho Chehab     Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
69a0bf528SMauro Carvalho Chehab 		       Marko Kohtala <marko.kohtala@luukku.com>
79a0bf528SMauro Carvalho Chehab 
89a0bf528SMauro Carvalho Chehab 
99a0bf528SMauro Carvalho Chehab */
109a0bf528SMauro Carvalho Chehab 
119a0bf528SMauro Carvalho Chehab #include <linux/init.h>
129a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
139a0bf528SMauro Carvalho Chehab #include <linux/module.h>
149a0bf528SMauro Carvalho Chehab #include <linux/string.h>
159a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
16fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
179a0bf528SMauro Carvalho Chehab #include "l64781.h"
189a0bf528SMauro Carvalho Chehab 
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab struct l64781_state {
219a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
229a0bf528SMauro Carvalho Chehab 	const struct l64781_config* config;
239a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab 	/* private demodulator data */
269a0bf528SMauro Carvalho Chehab 	unsigned int first:1;
279a0bf528SMauro Carvalho Chehab };
289a0bf528SMauro Carvalho Chehab 
299a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
309a0bf528SMauro Carvalho Chehab 	do { \
319a0bf528SMauro Carvalho Chehab 		if (debug) printk(KERN_DEBUG "l64781: " args); \
329a0bf528SMauro Carvalho Chehab 	} while (0)
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab static int debug;
359a0bf528SMauro Carvalho Chehab 
369a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
379a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab 
l64781_writereg(struct l64781_state * state,u8 reg,u8 data)409a0bf528SMauro Carvalho Chehab static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
419a0bf528SMauro Carvalho Chehab {
429a0bf528SMauro Carvalho Chehab 	int ret;
439a0bf528SMauro Carvalho Chehab 	u8 buf [] = { reg, data };
449a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
459a0bf528SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab 	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
479a0bf528SMauro Carvalho Chehab 		dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
489a0bf528SMauro Carvalho Chehab 			 __func__, reg, ret);
499a0bf528SMauro Carvalho Chehab 
509a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -1 : 0;
519a0bf528SMauro Carvalho Chehab }
529a0bf528SMauro Carvalho Chehab 
l64781_readreg(struct l64781_state * state,u8 reg)539a0bf528SMauro Carvalho Chehab static int l64781_readreg (struct l64781_state* state, u8 reg)
549a0bf528SMauro Carvalho Chehab {
559a0bf528SMauro Carvalho Chehab 	int ret;
569a0bf528SMauro Carvalho Chehab 	u8 b0 [] = { reg };
579a0bf528SMauro Carvalho Chehab 	u8 b1 [] = { 0 };
589a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
599a0bf528SMauro Carvalho Chehab 			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
629a0bf528SMauro Carvalho Chehab 
639a0bf528SMauro Carvalho Chehab 	if (ret != 2) return ret;
649a0bf528SMauro Carvalho Chehab 
659a0bf528SMauro Carvalho Chehab 	return b1[0];
669a0bf528SMauro Carvalho Chehab }
679a0bf528SMauro Carvalho Chehab 
apply_tps(struct l64781_state * state)689a0bf528SMauro Carvalho Chehab static void apply_tps (struct l64781_state* state)
699a0bf528SMauro Carvalho Chehab {
709a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x2a, 0x00);
719a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x2a, 0x01);
729a0bf528SMauro Carvalho Chehab 
739a0bf528SMauro Carvalho Chehab 	/* This here is a little bit questionable because it enables
749a0bf528SMauro Carvalho Chehab 	   the automatic update of TPS registers. I think we'd need to
759a0bf528SMauro Carvalho Chehab 	   handle the IRQ from FE to update some other registers as
769a0bf528SMauro Carvalho Chehab 	   well, or at least implement some magic to tuning to correct
779a0bf528SMauro Carvalho Chehab 	   to the TPS received from transmission. */
789a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x2a, 0x02);
799a0bf528SMauro Carvalho Chehab }
809a0bf528SMauro Carvalho Chehab 
819a0bf528SMauro Carvalho Chehab 
reset_afc(struct l64781_state * state)829a0bf528SMauro Carvalho Chehab static void reset_afc (struct l64781_state* state)
839a0bf528SMauro Carvalho Chehab {
849a0bf528SMauro Carvalho Chehab 	/* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
859a0bf528SMauro Carvalho Chehab 	   timing offset */
869a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
879a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x08, 0);    /* AFC INIT FREQ */
889a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x09, 0);
899a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x0a, 0);
909a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x07, 0x8e);
919a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x0e, 0);    /* AGC gain to zero in beginning */
929a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x11, 0x80); /* stall TIM */
939a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x10, 0);    /* TIM_OFFSET_LSB */
949a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x12, 0);
959a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x13, 0);
969a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x11, 0x00);
979a0bf528SMauro Carvalho Chehab }
989a0bf528SMauro Carvalho Chehab 
reset_and_configure(struct l64781_state * state)999a0bf528SMauro Carvalho Chehab static int reset_and_configure (struct l64781_state* state)
1009a0bf528SMauro Carvalho Chehab {
1019a0bf528SMauro Carvalho Chehab 	u8 buf [] = { 0x06 };
1029a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
1039a0bf528SMauro Carvalho Chehab 	// NOTE: this is correct in writing to address 0x00
1049a0bf528SMauro Carvalho Chehab 
1059a0bf528SMauro Carvalho Chehab 	return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
1069a0bf528SMauro Carvalho Chehab }
1079a0bf528SMauro Carvalho Chehab 
apply_frontend_param(struct dvb_frontend * fe)1089a0bf528SMauro Carvalho Chehab static int apply_frontend_param(struct dvb_frontend *fe)
1099a0bf528SMauro Carvalho Chehab {
1109a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
1119a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
1129a0bf528SMauro Carvalho Chehab 	/* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
1139a0bf528SMauro Carvalho Chehab 	static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
1149a0bf528SMauro Carvalho Chehab 	/* QPSK, QAM_16, QAM_64 */
1159a0bf528SMauro Carvalho Chehab 	static const u8 qam_tab [] = { 2, 4, 0, 6 };
1169a0bf528SMauro Carvalho Chehab 	static const u8 guard_tab [] = { 1, 2, 4, 8 };
1179a0bf528SMauro Carvalho Chehab 	/* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
1189a0bf528SMauro Carvalho Chehab 	static const u32 ppm = 8000;
1199a0bf528SMauro Carvalho Chehab 	u32 ddfs_offset_fixed;
1209a0bf528SMauro Carvalho Chehab /*	u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
1219a0bf528SMauro Carvalho Chehab /*			bw_tab[p->bandWidth]<<10)/15625; */
1229a0bf528SMauro Carvalho Chehab 	u32 init_freq;
1239a0bf528SMauro Carvalho Chehab 	u32 spi_bias;
1249a0bf528SMauro Carvalho Chehab 	u8 val0x04;
1259a0bf528SMauro Carvalho Chehab 	u8 val0x05;
1269a0bf528SMauro Carvalho Chehab 	u8 val0x06;
1279a0bf528SMauro Carvalho Chehab 	int bw;
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab 	switch (p->bandwidth_hz) {
1309a0bf528SMauro Carvalho Chehab 	case 8000000:
1319a0bf528SMauro Carvalho Chehab 		bw = 8;
1329a0bf528SMauro Carvalho Chehab 		break;
1339a0bf528SMauro Carvalho Chehab 	case 7000000:
1349a0bf528SMauro Carvalho Chehab 		bw = 7;
1359a0bf528SMauro Carvalho Chehab 		break;
1369a0bf528SMauro Carvalho Chehab 	case 6000000:
1379a0bf528SMauro Carvalho Chehab 		bw = 6;
1389a0bf528SMauro Carvalho Chehab 		break;
1399a0bf528SMauro Carvalho Chehab 	default:
1409a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1419a0bf528SMauro Carvalho Chehab 	}
1429a0bf528SMauro Carvalho Chehab 
1439a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
1449a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
1459a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
1469a0bf528SMauro Carvalho Chehab 	}
1479a0bf528SMauro Carvalho Chehab 
1489a0bf528SMauro Carvalho Chehab 	if (p->inversion != INVERSION_ON &&
1499a0bf528SMauro Carvalho Chehab 	    p->inversion != INVERSION_OFF)
1509a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1519a0bf528SMauro Carvalho Chehab 
1529a0bf528SMauro Carvalho Chehab 	if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
1539a0bf528SMauro Carvalho Chehab 	    p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
1549a0bf528SMauro Carvalho Chehab 	    p->code_rate_HP != FEC_7_8)
1559a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1569a0bf528SMauro Carvalho Chehab 
1579a0bf528SMauro Carvalho Chehab 	if (p->hierarchy != HIERARCHY_NONE &&
1589a0bf528SMauro Carvalho Chehab 	    (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
1599a0bf528SMauro Carvalho Chehab 	     p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
1609a0bf528SMauro Carvalho Chehab 	     p->code_rate_LP != FEC_7_8))
1619a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1629a0bf528SMauro Carvalho Chehab 
1639a0bf528SMauro Carvalho Chehab 	if (p->modulation != QPSK && p->modulation != QAM_16 &&
1649a0bf528SMauro Carvalho Chehab 	    p->modulation != QAM_64)
1659a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1669a0bf528SMauro Carvalho Chehab 
1679a0bf528SMauro Carvalho Chehab 	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
1689a0bf528SMauro Carvalho Chehab 	    p->transmission_mode != TRANSMISSION_MODE_8K)
1699a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1709a0bf528SMauro Carvalho Chehab 
171830e4b55SMauro Carvalho Chehab 	if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
1729a0bf528SMauro Carvalho Chehab 	    p->guard_interval > GUARD_INTERVAL_1_4)
1739a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1749a0bf528SMauro Carvalho Chehab 
175830e4b55SMauro Carvalho Chehab 	if ((int)p->hierarchy < HIERARCHY_NONE ||
1769a0bf528SMauro Carvalho Chehab 	    p->hierarchy > HIERARCHY_4)
1779a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab 	ddfs_offset_fixed = 0x4000-(ppm<<16)/bw/1000000;
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab 	/* This works up to 20000 ppm, it overflows if too large ppm! */
1829a0bf528SMauro Carvalho Chehab 	init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
1839a0bf528SMauro Carvalho Chehab 			bw & 0xFFFFFF);
1849a0bf528SMauro Carvalho Chehab 
1859a0bf528SMauro Carvalho Chehab 	/* SPI bias calculation is slightly modified to fit in 32bit */
1869a0bf528SMauro Carvalho Chehab 	/* will work for high ppm only... */
1879a0bf528SMauro Carvalho Chehab 	spi_bias = 378 * (1 << 10);
1889a0bf528SMauro Carvalho Chehab 	spi_bias *= 16;
1899a0bf528SMauro Carvalho Chehab 	spi_bias *= bw;
1909a0bf528SMauro Carvalho Chehab 	spi_bias *= qam_tab[p->modulation];
1919a0bf528SMauro Carvalho Chehab 	spi_bias /= p->code_rate_HP + 1;
1929a0bf528SMauro Carvalho Chehab 	spi_bias /= (guard_tab[p->guard_interval] + 32);
1939a0bf528SMauro Carvalho Chehab 	spi_bias *= 1000;
1949a0bf528SMauro Carvalho Chehab 	spi_bias /= 1000 + ppm/1000;
1959a0bf528SMauro Carvalho Chehab 	spi_bias *= p->code_rate_HP;
1969a0bf528SMauro Carvalho Chehab 
1979a0bf528SMauro Carvalho Chehab 	val0x04 = (p->transmission_mode << 2) | p->guard_interval;
1989a0bf528SMauro Carvalho Chehab 	val0x05 = fec_tab[p->code_rate_HP];
1999a0bf528SMauro Carvalho Chehab 
2009a0bf528SMauro Carvalho Chehab 	if (p->hierarchy != HIERARCHY_NONE)
2019a0bf528SMauro Carvalho Chehab 		val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
2029a0bf528SMauro Carvalho Chehab 
2039a0bf528SMauro Carvalho Chehab 	val0x06 = (p->hierarchy << 2) | p->modulation;
2049a0bf528SMauro Carvalho Chehab 
2059a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x04, val0x04);
2069a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x05, val0x05);
2079a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x06, val0x06);
2089a0bf528SMauro Carvalho Chehab 
2099a0bf528SMauro Carvalho Chehab 	reset_afc (state);
2109a0bf528SMauro Carvalho Chehab 
2119a0bf528SMauro Carvalho Chehab 	/* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
2129a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x15,
2139a0bf528SMauro Carvalho Chehab 			 p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
2149a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x16, init_freq & 0xff);
2159a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff);
2169a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff);
2179a0bf528SMauro Carvalho Chehab 
2189a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x1b, spi_bias & 0xff);
2199a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff);
2209a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) |
2219a0bf528SMauro Carvalho Chehab 		(p->inversion == INVERSION_ON ? 0x80 : 0x00));
2229a0bf528SMauro Carvalho Chehab 
2239a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff);
2249a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
2259a0bf528SMauro Carvalho Chehab 
2269a0bf528SMauro Carvalho Chehab 	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
2279a0bf528SMauro Carvalho Chehab 	l64781_readreg (state, 0x01);  /*  dto. */
2289a0bf528SMauro Carvalho Chehab 
2299a0bf528SMauro Carvalho Chehab 	apply_tps (state);
2309a0bf528SMauro Carvalho Chehab 
2319a0bf528SMauro Carvalho Chehab 	return 0;
2329a0bf528SMauro Carvalho Chehab }
2339a0bf528SMauro Carvalho Chehab 
get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * p)2347e3e68bcSMauro Carvalho Chehab static int get_frontend(struct dvb_frontend *fe,
2357e3e68bcSMauro Carvalho Chehab 			struct dtv_frontend_properties *p)
2369a0bf528SMauro Carvalho Chehab {
2379a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
2389a0bf528SMauro Carvalho Chehab 	int tmp;
2399a0bf528SMauro Carvalho Chehab 
2409a0bf528SMauro Carvalho Chehab 
2419a0bf528SMauro Carvalho Chehab 	tmp = l64781_readreg(state, 0x04);
2429a0bf528SMauro Carvalho Chehab 	switch(tmp & 3) {
2439a0bf528SMauro Carvalho Chehab 	case 0:
2449a0bf528SMauro Carvalho Chehab 		p->guard_interval = GUARD_INTERVAL_1_32;
2459a0bf528SMauro Carvalho Chehab 		break;
2469a0bf528SMauro Carvalho Chehab 	case 1:
2479a0bf528SMauro Carvalho Chehab 		p->guard_interval = GUARD_INTERVAL_1_16;
2489a0bf528SMauro Carvalho Chehab 		break;
2499a0bf528SMauro Carvalho Chehab 	case 2:
2509a0bf528SMauro Carvalho Chehab 		p->guard_interval = GUARD_INTERVAL_1_8;
2519a0bf528SMauro Carvalho Chehab 		break;
2529a0bf528SMauro Carvalho Chehab 	case 3:
2539a0bf528SMauro Carvalho Chehab 		p->guard_interval = GUARD_INTERVAL_1_4;
2549a0bf528SMauro Carvalho Chehab 		break;
2559a0bf528SMauro Carvalho Chehab 	}
2569a0bf528SMauro Carvalho Chehab 	switch((tmp >> 2) & 3) {
2579a0bf528SMauro Carvalho Chehab 	case 0:
2589a0bf528SMauro Carvalho Chehab 		p->transmission_mode = TRANSMISSION_MODE_2K;
2599a0bf528SMauro Carvalho Chehab 		break;
2609a0bf528SMauro Carvalho Chehab 	case 1:
2619a0bf528SMauro Carvalho Chehab 		p->transmission_mode = TRANSMISSION_MODE_8K;
2629a0bf528SMauro Carvalho Chehab 		break;
2639a0bf528SMauro Carvalho Chehab 	default:
2649a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "Unexpected value for transmission_mode\n");
2659a0bf528SMauro Carvalho Chehab 	}
2669a0bf528SMauro Carvalho Chehab 
2679a0bf528SMauro Carvalho Chehab 	tmp = l64781_readreg(state, 0x05);
2689a0bf528SMauro Carvalho Chehab 	switch(tmp & 7) {
2699a0bf528SMauro Carvalho Chehab 	case 0:
2709a0bf528SMauro Carvalho Chehab 		p->code_rate_HP = FEC_1_2;
2719a0bf528SMauro Carvalho Chehab 		break;
2729a0bf528SMauro Carvalho Chehab 	case 1:
2739a0bf528SMauro Carvalho Chehab 		p->code_rate_HP = FEC_2_3;
2749a0bf528SMauro Carvalho Chehab 		break;
2759a0bf528SMauro Carvalho Chehab 	case 2:
2769a0bf528SMauro Carvalho Chehab 		p->code_rate_HP = FEC_3_4;
2779a0bf528SMauro Carvalho Chehab 		break;
2789a0bf528SMauro Carvalho Chehab 	case 3:
2799a0bf528SMauro Carvalho Chehab 		p->code_rate_HP = FEC_5_6;
2809a0bf528SMauro Carvalho Chehab 		break;
2819a0bf528SMauro Carvalho Chehab 	case 4:
2829a0bf528SMauro Carvalho Chehab 		p->code_rate_HP = FEC_7_8;
2839a0bf528SMauro Carvalho Chehab 		break;
2849a0bf528SMauro Carvalho Chehab 	default:
2859a0bf528SMauro Carvalho Chehab 		printk("Unexpected value for code_rate_HP\n");
2869a0bf528SMauro Carvalho Chehab 	}
2879a0bf528SMauro Carvalho Chehab 	switch((tmp >> 3) & 7) {
2889a0bf528SMauro Carvalho Chehab 	case 0:
2899a0bf528SMauro Carvalho Chehab 		p->code_rate_LP = FEC_1_2;
2909a0bf528SMauro Carvalho Chehab 		break;
2919a0bf528SMauro Carvalho Chehab 	case 1:
2929a0bf528SMauro Carvalho Chehab 		p->code_rate_LP = FEC_2_3;
2939a0bf528SMauro Carvalho Chehab 		break;
2949a0bf528SMauro Carvalho Chehab 	case 2:
2959a0bf528SMauro Carvalho Chehab 		p->code_rate_LP = FEC_3_4;
2969a0bf528SMauro Carvalho Chehab 		break;
2979a0bf528SMauro Carvalho Chehab 	case 3:
2989a0bf528SMauro Carvalho Chehab 		p->code_rate_LP = FEC_5_6;
2999a0bf528SMauro Carvalho Chehab 		break;
3009a0bf528SMauro Carvalho Chehab 	case 4:
3019a0bf528SMauro Carvalho Chehab 		p->code_rate_LP = FEC_7_8;
3029a0bf528SMauro Carvalho Chehab 		break;
3039a0bf528SMauro Carvalho Chehab 	default:
3049a0bf528SMauro Carvalho Chehab 		printk("Unexpected value for code_rate_LP\n");
3059a0bf528SMauro Carvalho Chehab 	}
3069a0bf528SMauro Carvalho Chehab 
3079a0bf528SMauro Carvalho Chehab 	tmp = l64781_readreg(state, 0x06);
3089a0bf528SMauro Carvalho Chehab 	switch(tmp & 3) {
3099a0bf528SMauro Carvalho Chehab 	case 0:
3109a0bf528SMauro Carvalho Chehab 		p->modulation = QPSK;
3119a0bf528SMauro Carvalho Chehab 		break;
3129a0bf528SMauro Carvalho Chehab 	case 1:
3139a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_16;
3149a0bf528SMauro Carvalho Chehab 		break;
3159a0bf528SMauro Carvalho Chehab 	case 2:
3169a0bf528SMauro Carvalho Chehab 		p->modulation = QAM_64;
3179a0bf528SMauro Carvalho Chehab 		break;
3189a0bf528SMauro Carvalho Chehab 	default:
3199a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "Unexpected value for modulation\n");
3209a0bf528SMauro Carvalho Chehab 	}
3219a0bf528SMauro Carvalho Chehab 	switch((tmp >> 2) & 7) {
3229a0bf528SMauro Carvalho Chehab 	case 0:
3239a0bf528SMauro Carvalho Chehab 		p->hierarchy = HIERARCHY_NONE;
3249a0bf528SMauro Carvalho Chehab 		break;
3259a0bf528SMauro Carvalho Chehab 	case 1:
3269a0bf528SMauro Carvalho Chehab 		p->hierarchy = HIERARCHY_1;
3279a0bf528SMauro Carvalho Chehab 		break;
3289a0bf528SMauro Carvalho Chehab 	case 2:
3299a0bf528SMauro Carvalho Chehab 		p->hierarchy = HIERARCHY_2;
3309a0bf528SMauro Carvalho Chehab 		break;
3319a0bf528SMauro Carvalho Chehab 	case 3:
3329a0bf528SMauro Carvalho Chehab 		p->hierarchy = HIERARCHY_4;
3339a0bf528SMauro Carvalho Chehab 		break;
3349a0bf528SMauro Carvalho Chehab 	default:
3359a0bf528SMauro Carvalho Chehab 		printk("Unexpected value for hierarchy\n");
3369a0bf528SMauro Carvalho Chehab 	}
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab 
3399a0bf528SMauro Carvalho Chehab 	tmp = l64781_readreg (state, 0x1d);
3409a0bf528SMauro Carvalho Chehab 	p->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
3419a0bf528SMauro Carvalho Chehab 
3429a0bf528SMauro Carvalho Chehab 	tmp = (int) (l64781_readreg (state, 0x08) |
3439a0bf528SMauro Carvalho Chehab 		     (l64781_readreg (state, 0x09) << 8) |
3449a0bf528SMauro Carvalho Chehab 		     (l64781_readreg (state, 0x0a) << 16));
3459a0bf528SMauro Carvalho Chehab 	p->frequency += tmp;
3469a0bf528SMauro Carvalho Chehab 
3479a0bf528SMauro Carvalho Chehab 	return 0;
3489a0bf528SMauro Carvalho Chehab }
3499a0bf528SMauro Carvalho Chehab 
l64781_read_status(struct dvb_frontend * fe,enum fe_status * status)3500df289a2SMauro Carvalho Chehab static int l64781_read_status(struct dvb_frontend *fe, enum fe_status *status)
3519a0bf528SMauro Carvalho Chehab {
3529a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
3539a0bf528SMauro Carvalho Chehab 	int sync = l64781_readreg (state, 0x32);
3549a0bf528SMauro Carvalho Chehab 	int gain = l64781_readreg (state, 0x0e);
3559a0bf528SMauro Carvalho Chehab 
3569a0bf528SMauro Carvalho Chehab 	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
3579a0bf528SMauro Carvalho Chehab 	l64781_readreg (state, 0x01);  /*  dto. */
3589a0bf528SMauro Carvalho Chehab 
3599a0bf528SMauro Carvalho Chehab 	*status = 0;
3609a0bf528SMauro Carvalho Chehab 
3619a0bf528SMauro Carvalho Chehab 	if (gain > 5)
3629a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
3639a0bf528SMauro Carvalho Chehab 
3649a0bf528SMauro Carvalho Chehab 	if (sync & 0x02) /* VCXO locked, this criteria should be ok */
3659a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
3669a0bf528SMauro Carvalho Chehab 
3679a0bf528SMauro Carvalho Chehab 	if (sync & 0x20)
3689a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
3699a0bf528SMauro Carvalho Chehab 
3709a0bf528SMauro Carvalho Chehab 	if (sync & 0x40)
3719a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
3729a0bf528SMauro Carvalho Chehab 
3739a0bf528SMauro Carvalho Chehab 	if (sync == 0x7f)
3749a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
3759a0bf528SMauro Carvalho Chehab 
3769a0bf528SMauro Carvalho Chehab 	return 0;
3779a0bf528SMauro Carvalho Chehab }
3789a0bf528SMauro Carvalho Chehab 
l64781_read_ber(struct dvb_frontend * fe,u32 * ber)3799a0bf528SMauro Carvalho Chehab static int l64781_read_ber(struct dvb_frontend* fe, u32* ber)
3809a0bf528SMauro Carvalho Chehab {
3819a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
3829a0bf528SMauro Carvalho Chehab 
3839a0bf528SMauro Carvalho Chehab 	/*   XXX FIXME: set up counting period (reg 0x26...0x28)
3849a0bf528SMauro Carvalho Chehab 	 */
3859a0bf528SMauro Carvalho Chehab 	*ber = l64781_readreg (state, 0x39)
3869a0bf528SMauro Carvalho Chehab 	    | (l64781_readreg (state, 0x3a) << 8);
3879a0bf528SMauro Carvalho Chehab 
3889a0bf528SMauro Carvalho Chehab 	return 0;
3899a0bf528SMauro Carvalho Chehab }
3909a0bf528SMauro Carvalho Chehab 
l64781_read_signal_strength(struct dvb_frontend * fe,u16 * signal_strength)3919a0bf528SMauro Carvalho Chehab static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
3929a0bf528SMauro Carvalho Chehab {
3939a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
3949a0bf528SMauro Carvalho Chehab 
3959a0bf528SMauro Carvalho Chehab 	u8 gain = l64781_readreg (state, 0x0e);
3969a0bf528SMauro Carvalho Chehab 	*signal_strength = (gain << 8) | gain;
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab 	return 0;
3999a0bf528SMauro Carvalho Chehab }
4009a0bf528SMauro Carvalho Chehab 
l64781_read_snr(struct dvb_frontend * fe,u16 * snr)4019a0bf528SMauro Carvalho Chehab static int l64781_read_snr(struct dvb_frontend* fe, u16* snr)
4029a0bf528SMauro Carvalho Chehab {
4039a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
4049a0bf528SMauro Carvalho Chehab 
4059a0bf528SMauro Carvalho Chehab 	u8 avg_quality = 0xff - l64781_readreg (state, 0x33);
4069a0bf528SMauro Carvalho Chehab 	*snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
4079a0bf528SMauro Carvalho Chehab 
4089a0bf528SMauro Carvalho Chehab 	return 0;
4099a0bf528SMauro Carvalho Chehab }
4109a0bf528SMauro Carvalho Chehab 
l64781_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)4119a0bf528SMauro Carvalho Chehab static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
4129a0bf528SMauro Carvalho Chehab {
4139a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
4149a0bf528SMauro Carvalho Chehab 
4159a0bf528SMauro Carvalho Chehab 	*ucblocks = l64781_readreg (state, 0x37)
4169a0bf528SMauro Carvalho Chehab 	   | (l64781_readreg (state, 0x38) << 8);
4179a0bf528SMauro Carvalho Chehab 
4189a0bf528SMauro Carvalho Chehab 	return 0;
4199a0bf528SMauro Carvalho Chehab }
4209a0bf528SMauro Carvalho Chehab 
l64781_sleep(struct dvb_frontend * fe)4219a0bf528SMauro Carvalho Chehab static int l64781_sleep(struct dvb_frontend* fe)
4229a0bf528SMauro Carvalho Chehab {
4239a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
4249a0bf528SMauro Carvalho Chehab 
4259a0bf528SMauro Carvalho Chehab 	/* Power down */
4269a0bf528SMauro Carvalho Chehab 	return l64781_writereg (state, 0x3e, 0x5a);
4279a0bf528SMauro Carvalho Chehab }
4289a0bf528SMauro Carvalho Chehab 
l64781_init(struct dvb_frontend * fe)4299a0bf528SMauro Carvalho Chehab static int l64781_init(struct dvb_frontend* fe)
4309a0bf528SMauro Carvalho Chehab {
4319a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
4329a0bf528SMauro Carvalho Chehab 
4339a0bf528SMauro Carvalho Chehab 	reset_and_configure (state);
4349a0bf528SMauro Carvalho Chehab 
4359a0bf528SMauro Carvalho Chehab 	/* Power up */
4369a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x3e, 0xa5);
4379a0bf528SMauro Carvalho Chehab 
4389a0bf528SMauro Carvalho Chehab 	/* Reset hard */
4399a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x2a, 0x04);
4409a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x2a, 0x00);
4419a0bf528SMauro Carvalho Chehab 
4429a0bf528SMauro Carvalho Chehab 	/* Set tuner specific things */
4439a0bf528SMauro Carvalho Chehab 	/* AFC_POL, set also in reset_afc */
4449a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x07, 0x8e);
4459a0bf528SMauro Carvalho Chehab 
4469a0bf528SMauro Carvalho Chehab 	/* Use internal ADC */
4479a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x0b, 0x81);
4489a0bf528SMauro Carvalho Chehab 
4499a0bf528SMauro Carvalho Chehab 	/* AGC loop gain, and polarity is positive */
4509a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x0c, 0x84);
4519a0bf528SMauro Carvalho Chehab 
4529a0bf528SMauro Carvalho Chehab 	/* Internal ADC outputs two's complement */
4539a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x0d, 0x8c);
4549a0bf528SMauro Carvalho Chehab 
4559a0bf528SMauro Carvalho Chehab 	/* With ppm=8000, it seems the DTR_SENSITIVITY will result in
4569a0bf528SMauro Carvalho Chehab 	   value of 2 with all possible bandwidths and guard
4579a0bf528SMauro Carvalho Chehab 	   intervals, which is the initial value anyway. */
4589a0bf528SMauro Carvalho Chehab 	/*l64781_writereg (state, 0x19, 0x92);*/
4599a0bf528SMauro Carvalho Chehab 
4609a0bf528SMauro Carvalho Chehab 	/* Everything is two's complement, soft bit and CSI_OUT too */
4619a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x1e, 0x09);
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 	/* delay a bit after first init attempt */
4649a0bf528SMauro Carvalho Chehab 	if (state->first) {
4659a0bf528SMauro Carvalho Chehab 		state->first = 0;
4669a0bf528SMauro Carvalho Chehab 		msleep(200);
4679a0bf528SMauro Carvalho Chehab 	}
4689a0bf528SMauro Carvalho Chehab 
4699a0bf528SMauro Carvalho Chehab 	return 0;
4709a0bf528SMauro Carvalho Chehab }
4719a0bf528SMauro Carvalho Chehab 
l64781_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * fesettings)4729a0bf528SMauro Carvalho Chehab static int l64781_get_tune_settings(struct dvb_frontend* fe,
4739a0bf528SMauro Carvalho Chehab 				    struct dvb_frontend_tune_settings* fesettings)
4749a0bf528SMauro Carvalho Chehab {
4759a0bf528SMauro Carvalho Chehab 	fesettings->min_delay_ms = 4000;
4769a0bf528SMauro Carvalho Chehab 	fesettings->step_size = 0;
4779a0bf528SMauro Carvalho Chehab 	fesettings->max_drift = 0;
4789a0bf528SMauro Carvalho Chehab 	return 0;
4799a0bf528SMauro Carvalho Chehab }
4809a0bf528SMauro Carvalho Chehab 
l64781_release(struct dvb_frontend * fe)4819a0bf528SMauro Carvalho Chehab static void l64781_release(struct dvb_frontend* fe)
4829a0bf528SMauro Carvalho Chehab {
4839a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = fe->demodulator_priv;
4849a0bf528SMauro Carvalho Chehab 	kfree(state);
4859a0bf528SMauro Carvalho Chehab }
4869a0bf528SMauro Carvalho Chehab 
487bd336e63SMax Kellermann static const struct dvb_frontend_ops l64781_ops;
4889a0bf528SMauro Carvalho Chehab 
l64781_attach(const struct l64781_config * config,struct i2c_adapter * i2c)4899a0bf528SMauro Carvalho Chehab struct dvb_frontend* l64781_attach(const struct l64781_config* config,
4909a0bf528SMauro Carvalho Chehab 				   struct i2c_adapter* i2c)
4919a0bf528SMauro Carvalho Chehab {
4929a0bf528SMauro Carvalho Chehab 	struct l64781_state* state = NULL;
4939a0bf528SMauro Carvalho Chehab 	int reg0x3e = -1;
4949a0bf528SMauro Carvalho Chehab 	u8 b0 [] = { 0x1a };
4959a0bf528SMauro Carvalho Chehab 	u8 b1 [] = { 0x00 };
4969a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 },
4979a0bf528SMauro Carvalho Chehab 			   { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
4989a0bf528SMauro Carvalho Chehab 
4999a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
5009a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL);
5019a0bf528SMauro Carvalho Chehab 	if (state == NULL) goto error;
5029a0bf528SMauro Carvalho Chehab 
5039a0bf528SMauro Carvalho Chehab 	/* setup the state */
5049a0bf528SMauro Carvalho Chehab 	state->config = config;
5059a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
5069a0bf528SMauro Carvalho Chehab 	state->first = 1;
5079a0bf528SMauro Carvalho Chehab 
508b95b0c98SMauro Carvalho Chehab 	/*
5099a0bf528SMauro Carvalho Chehab 	 *  the L64781 won't show up before we send the reset_and_configure()
5109a0bf528SMauro Carvalho Chehab 	 *  broadcast. If nothing responds there is no L64781 on the bus...
5119a0bf528SMauro Carvalho Chehab 	 */
5129a0bf528SMauro Carvalho Chehab 	if (reset_and_configure(state) < 0) {
5139a0bf528SMauro Carvalho Chehab 		dprintk("No response to reset and configure broadcast...\n");
5149a0bf528SMauro Carvalho Chehab 		goto error;
5159a0bf528SMauro Carvalho Chehab 	}
5169a0bf528SMauro Carvalho Chehab 
5179a0bf528SMauro Carvalho Chehab 	/* The chip always responds to reads */
5189a0bf528SMauro Carvalho Chehab 	if (i2c_transfer(state->i2c, msg, 2) != 2) {
5199a0bf528SMauro Carvalho Chehab 		dprintk("No response to read on I2C bus\n");
5209a0bf528SMauro Carvalho Chehab 		goto error;
5219a0bf528SMauro Carvalho Chehab 	}
5229a0bf528SMauro Carvalho Chehab 
5239a0bf528SMauro Carvalho Chehab 	/* Save current register contents for bailout */
5249a0bf528SMauro Carvalho Chehab 	reg0x3e = l64781_readreg(state, 0x3e);
5259a0bf528SMauro Carvalho Chehab 
5269a0bf528SMauro Carvalho Chehab 	/* Reading the POWER_DOWN register always returns 0 */
5279a0bf528SMauro Carvalho Chehab 	if (reg0x3e != 0) {
5289a0bf528SMauro Carvalho Chehab 		dprintk("Device doesn't look like L64781\n");
5299a0bf528SMauro Carvalho Chehab 		goto error;
5309a0bf528SMauro Carvalho Chehab 	}
5319a0bf528SMauro Carvalho Chehab 
5329a0bf528SMauro Carvalho Chehab 	/* Turn the chip off */
5339a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x3e, 0x5a);
5349a0bf528SMauro Carvalho Chehab 
5359a0bf528SMauro Carvalho Chehab 	/* Responds to all reads with 0 */
5369a0bf528SMauro Carvalho Chehab 	if (l64781_readreg(state, 0x1a) != 0) {
537715b703dSColin Ian King 		dprintk("Read 1 returned unexpected value\n");
5389a0bf528SMauro Carvalho Chehab 		goto error;
5399a0bf528SMauro Carvalho Chehab 	}
5409a0bf528SMauro Carvalho Chehab 
5419a0bf528SMauro Carvalho Chehab 	/* Turn the chip on */
5429a0bf528SMauro Carvalho Chehab 	l64781_writereg (state, 0x3e, 0xa5);
5439a0bf528SMauro Carvalho Chehab 
5449a0bf528SMauro Carvalho Chehab 	/* Responds with register default value */
5459a0bf528SMauro Carvalho Chehab 	if (l64781_readreg(state, 0x1a) != 0xa1) {
546715b703dSColin Ian King 		dprintk("Read 2 returned unexpected value\n");
5479a0bf528SMauro Carvalho Chehab 		goto error;
5489a0bf528SMauro Carvalho Chehab 	}
5499a0bf528SMauro Carvalho Chehab 
5509a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
5519a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &l64781_ops, sizeof(struct dvb_frontend_ops));
5529a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
5539a0bf528SMauro Carvalho Chehab 	return &state->frontend;
5549a0bf528SMauro Carvalho Chehab 
5559a0bf528SMauro Carvalho Chehab error:
5569a0bf528SMauro Carvalho Chehab 	if (reg0x3e >= 0)
5579a0bf528SMauro Carvalho Chehab 		l64781_writereg (state, 0x3e, reg0x3e);  /* restore reg 0x3e */
5589a0bf528SMauro Carvalho Chehab 	kfree(state);
5599a0bf528SMauro Carvalho Chehab 	return NULL;
5609a0bf528SMauro Carvalho Chehab }
5619a0bf528SMauro Carvalho Chehab 
562bd336e63SMax Kellermann static const struct dvb_frontend_ops l64781_ops = {
5639a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DVBT },
5649a0bf528SMauro Carvalho Chehab 	.info = {
5659a0bf528SMauro Carvalho Chehab 		.name = "LSI L64781 DVB-T",
566f1b1eabfSMauro Carvalho Chehab 	/*	.frequency_min_hz = ???,*/
567f1b1eabfSMauro Carvalho Chehab 	/*	.frequency_max_hz = ???,*/
568f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 166666,
5699a0bf528SMauro Carvalho Chehab 	/*      .symbol_rate_tolerance = ???,*/
5709a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
5719a0bf528SMauro Carvalho Chehab 		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
5729a0bf528SMauro Carvalho Chehab 		      FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
5739a0bf528SMauro Carvalho Chehab 		      FE_CAN_MUTE_TS
5749a0bf528SMauro Carvalho Chehab 	},
5759a0bf528SMauro Carvalho Chehab 
5769a0bf528SMauro Carvalho Chehab 	.release = l64781_release,
5779a0bf528SMauro Carvalho Chehab 
5789a0bf528SMauro Carvalho Chehab 	.init = l64781_init,
5799a0bf528SMauro Carvalho Chehab 	.sleep = l64781_sleep,
5809a0bf528SMauro Carvalho Chehab 
5819a0bf528SMauro Carvalho Chehab 	.set_frontend = apply_frontend_param,
5829a0bf528SMauro Carvalho Chehab 	.get_frontend = get_frontend,
5839a0bf528SMauro Carvalho Chehab 	.get_tune_settings = l64781_get_tune_settings,
5849a0bf528SMauro Carvalho Chehab 
5859a0bf528SMauro Carvalho Chehab 	.read_status = l64781_read_status,
5869a0bf528SMauro Carvalho Chehab 	.read_ber = l64781_read_ber,
5879a0bf528SMauro Carvalho Chehab 	.read_signal_strength = l64781_read_signal_strength,
5889a0bf528SMauro Carvalho Chehab 	.read_snr = l64781_read_snr,
5899a0bf528SMauro Carvalho Chehab 	.read_ucblocks = l64781_read_ucblocks,
5909a0bf528SMauro Carvalho Chehab };
5919a0bf528SMauro Carvalho Chehab 
5929a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
5939a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
5949a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
5959a0bf528SMauro Carvalho Chehab 
596*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(l64781_attach);
597