19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 2008 Sirius International (Hong Kong) Limited
59a0bf528SMauro Carvalho Chehab 	Timothy Lee <timothy.lee@siriushk.com>
69a0bf528SMauro Carvalho Chehab 
79a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
89a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
99a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
109a0bf528SMauro Carvalho Chehab     (at your option) any later version.
119a0bf528SMauro Carvalho Chehab 
129a0bf528SMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
139a0bf528SMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
149a0bf528SMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
159a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
189a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
199a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab */
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
249a0bf528SMauro Carvalho Chehab #include <linux/init.h>
259a0bf528SMauro Carvalho Chehab #include <linux/module.h>
269a0bf528SMauro Carvalho Chehab #include <linux/string.h>
279a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
28fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
299a0bf528SMauro Carvalho Chehab #include "lgs8gl5.h"
309a0bf528SMauro Carvalho Chehab 
319a0bf528SMauro Carvalho Chehab 
329a0bf528SMauro Carvalho Chehab #define REG_RESET		0x02
339a0bf528SMauro Carvalho Chehab #define REG_RESET_OFF			0x01
349a0bf528SMauro Carvalho Chehab #define REG_03			0x03
359a0bf528SMauro Carvalho Chehab #define REG_04			0x04
369a0bf528SMauro Carvalho Chehab #define REG_07			0x07
379a0bf528SMauro Carvalho Chehab #define REG_09			0x09
389a0bf528SMauro Carvalho Chehab #define REG_0A			0x0a
399a0bf528SMauro Carvalho Chehab #define REG_0B			0x0b
409a0bf528SMauro Carvalho Chehab #define REG_0C			0x0c
419a0bf528SMauro Carvalho Chehab #define REG_37			0x37
429a0bf528SMauro Carvalho Chehab #define REG_STRENGTH		0x4b
439a0bf528SMauro Carvalho Chehab #define REG_STRENGTH_MASK		0x7f
449a0bf528SMauro Carvalho Chehab #define REG_STRENGTH_CARRIER		0x80
459a0bf528SMauro Carvalho Chehab #define REG_INVERSION		0x7c
469a0bf528SMauro Carvalho Chehab #define REG_INVERSION_ON		0x80
479a0bf528SMauro Carvalho Chehab #define REG_7D			0x7d
489a0bf528SMauro Carvalho Chehab #define REG_7E			0x7e
499a0bf528SMauro Carvalho Chehab #define REG_A2			0xa2
509a0bf528SMauro Carvalho Chehab #define REG_STATUS		0xa4
519a0bf528SMauro Carvalho Chehab #define REG_STATUS_SYNC		0x04
529a0bf528SMauro Carvalho Chehab #define REG_STATUS_LOCK		0x01
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab 
559a0bf528SMauro Carvalho Chehab struct lgs8gl5_state {
569a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
579a0bf528SMauro Carvalho Chehab 	const struct lgs8gl5_config *config;
589a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
599a0bf528SMauro Carvalho Chehab };
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 
629a0bf528SMauro Carvalho Chehab static int debug;
639a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
649a0bf528SMauro Carvalho Chehab 	do { \
659a0bf528SMauro Carvalho Chehab 		if (debug) \
669a0bf528SMauro Carvalho Chehab 			printk(KERN_DEBUG "lgs8gl5: " args); \
679a0bf528SMauro Carvalho Chehab 	} while (0)
689a0bf528SMauro Carvalho Chehab 
699a0bf528SMauro Carvalho Chehab 
709a0bf528SMauro Carvalho Chehab /* Writes into demod's register */
719a0bf528SMauro Carvalho Chehab static int
729a0bf528SMauro Carvalho Chehab lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
739a0bf528SMauro Carvalho Chehab {
749a0bf528SMauro Carvalho Chehab 	int ret;
759a0bf528SMauro Carvalho Chehab 	u8 buf[] = {reg, data};
769a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
779a0bf528SMauro Carvalho Chehab 		.addr  = state->config->demod_address,
789a0bf528SMauro Carvalho Chehab 		.flags = 0,
799a0bf528SMauro Carvalho Chehab 		.buf   = buf,
809a0bf528SMauro Carvalho Chehab 		.len   = 2
819a0bf528SMauro Carvalho Chehab 	};
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
849a0bf528SMauro Carvalho Chehab 	if (ret != 1)
859a0bf528SMauro Carvalho Chehab 		dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
869a0bf528SMauro Carvalho Chehab 			__func__, reg, data, ret);
879a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -1 : 0;
889a0bf528SMauro Carvalho Chehab }
899a0bf528SMauro Carvalho Chehab 
909a0bf528SMauro Carvalho Chehab 
919a0bf528SMauro Carvalho Chehab /* Reads from demod's register */
929a0bf528SMauro Carvalho Chehab static int
939a0bf528SMauro Carvalho Chehab lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
949a0bf528SMauro Carvalho Chehab {
959a0bf528SMauro Carvalho Chehab 	int ret;
969a0bf528SMauro Carvalho Chehab 	u8 b0[] = {reg};
979a0bf528SMauro Carvalho Chehab 	u8 b1[] = {0};
989a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[2] = {
999a0bf528SMauro Carvalho Chehab 		{
1009a0bf528SMauro Carvalho Chehab 			.addr  = state->config->demod_address,
1019a0bf528SMauro Carvalho Chehab 			.flags = 0,
1029a0bf528SMauro Carvalho Chehab 			.buf   = b0,
1039a0bf528SMauro Carvalho Chehab 			.len   = 1
1049a0bf528SMauro Carvalho Chehab 		},
1059a0bf528SMauro Carvalho Chehab 		{
1069a0bf528SMauro Carvalho Chehab 			.addr  = state->config->demod_address,
1079a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
1089a0bf528SMauro Carvalho Chehab 			.buf   = b1,
1099a0bf528SMauro Carvalho Chehab 			.len   = 1
1109a0bf528SMauro Carvalho Chehab 		}
1119a0bf528SMauro Carvalho Chehab 	};
1129a0bf528SMauro Carvalho Chehab 
1139a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
1149a0bf528SMauro Carvalho Chehab 	if (ret != 2)
1159a0bf528SMauro Carvalho Chehab 		return -EIO;
1169a0bf528SMauro Carvalho Chehab 
1179a0bf528SMauro Carvalho Chehab 	return b1[0];
1189a0bf528SMauro Carvalho Chehab }
1199a0bf528SMauro Carvalho Chehab 
1209a0bf528SMauro Carvalho Chehab 
1219a0bf528SMauro Carvalho Chehab static int
1229a0bf528SMauro Carvalho Chehab lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
1239a0bf528SMauro Carvalho Chehab {
1249a0bf528SMauro Carvalho Chehab 	lgs8gl5_read_reg(state, reg);
1259a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, reg, data);
1269a0bf528SMauro Carvalho Chehab 	return 0;
1279a0bf528SMauro Carvalho Chehab }
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab 
1309a0bf528SMauro Carvalho Chehab /* Writes into alternate device's register */
1319a0bf528SMauro Carvalho Chehab /* TODO:  Find out what that device is for! */
1329a0bf528SMauro Carvalho Chehab static int
1339a0bf528SMauro Carvalho Chehab lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
1349a0bf528SMauro Carvalho Chehab {
1359a0bf528SMauro Carvalho Chehab 	int ret;
1369a0bf528SMauro Carvalho Chehab 	u8 b0[] = {reg};
1379a0bf528SMauro Carvalho Chehab 	u8 b1[] = {0};
1389a0bf528SMauro Carvalho Chehab 	u8 b2[] = {reg, data};
1399a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[3] = {
1409a0bf528SMauro Carvalho Chehab 		{
1419a0bf528SMauro Carvalho Chehab 			.addr  = state->config->demod_address + 2,
1429a0bf528SMauro Carvalho Chehab 			.flags = 0,
1439a0bf528SMauro Carvalho Chehab 			.buf   = b0,
1449a0bf528SMauro Carvalho Chehab 			.len   = 1
1459a0bf528SMauro Carvalho Chehab 		},
1469a0bf528SMauro Carvalho Chehab 		{
1479a0bf528SMauro Carvalho Chehab 			.addr  = state->config->demod_address + 2,
1489a0bf528SMauro Carvalho Chehab 			.flags = I2C_M_RD,
1499a0bf528SMauro Carvalho Chehab 			.buf   = b1,
1509a0bf528SMauro Carvalho Chehab 			.len   = 1
1519a0bf528SMauro Carvalho Chehab 		},
1529a0bf528SMauro Carvalho Chehab 		{
1539a0bf528SMauro Carvalho Chehab 			.addr  = state->config->demod_address + 2,
1549a0bf528SMauro Carvalho Chehab 			.flags = 0,
1559a0bf528SMauro Carvalho Chehab 			.buf   = b2,
1569a0bf528SMauro Carvalho Chehab 			.len   = 2
1579a0bf528SMauro Carvalho Chehab 		},
1589a0bf528SMauro Carvalho Chehab 	};
1599a0bf528SMauro Carvalho Chehab 
1609a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 3);
1619a0bf528SMauro Carvalho Chehab 	return (ret != 3) ? -1 : 0;
1629a0bf528SMauro Carvalho Chehab }
1639a0bf528SMauro Carvalho Chehab 
1649a0bf528SMauro Carvalho Chehab 
1659a0bf528SMauro Carvalho Chehab static void
1669a0bf528SMauro Carvalho Chehab lgs8gl5_soft_reset(struct lgs8gl5_state *state)
1679a0bf528SMauro Carvalho Chehab {
1689a0bf528SMauro Carvalho Chehab 	u8 val;
1699a0bf528SMauro Carvalho Chehab 
1709a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
1719a0bf528SMauro Carvalho Chehab 
1729a0bf528SMauro Carvalho Chehab 	val = lgs8gl5_read_reg(state, REG_RESET);
1739a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
1749a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
1759a0bf528SMauro Carvalho Chehab 	msleep(5);
1769a0bf528SMauro Carvalho Chehab }
1779a0bf528SMauro Carvalho Chehab 
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab /* Starts demodulation */
1809a0bf528SMauro Carvalho Chehab static void
1819a0bf528SMauro Carvalho Chehab lgs8gl5_start_demod(struct lgs8gl5_state *state)
1829a0bf528SMauro Carvalho Chehab {
1839a0bf528SMauro Carvalho Chehab 	u8  val;
1849a0bf528SMauro Carvalho Chehab 	int n;
1859a0bf528SMauro Carvalho Chehab 
1869a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
1879a0bf528SMauro Carvalho Chehab 
1889a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
1899a0bf528SMauro Carvalho Chehab 	lgs8gl5_soft_reset(state);
1909a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_07, 0x10);
1919a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_07, 0x10);
1929a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_09, 0x0e);
1939a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0A, 0xe5);
1949a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0B, 0x35);
1959a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0C, 0x30);
1969a0bf528SMauro Carvalho Chehab 
1979a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_03, 0x00);
1989a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_7E, 0x01);
1999a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
2009a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_04, 0x02);
2019a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_37, 0x01);
2029a0bf528SMauro Carvalho Chehab 	lgs8gl5_soft_reset(state);
2039a0bf528SMauro Carvalho Chehab 
2049a0bf528SMauro Carvalho Chehab 	/* Wait for carrier */
2059a0bf528SMauro Carvalho Chehab 	for (n = 0;  n < 10;  n++) {
2069a0bf528SMauro Carvalho Chehab 		val = lgs8gl5_read_reg(state, REG_STRENGTH);
2079a0bf528SMauro Carvalho Chehab 		dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
2089a0bf528SMauro Carvalho Chehab 		if (val & REG_STRENGTH_CARRIER)
2099a0bf528SMauro Carvalho Chehab 			break;
2109a0bf528SMauro Carvalho Chehab 		msleep(4);
2119a0bf528SMauro Carvalho Chehab 	}
2129a0bf528SMauro Carvalho Chehab 	if (!(val & REG_STRENGTH_CARRIER))
2139a0bf528SMauro Carvalho Chehab 		return;
2149a0bf528SMauro Carvalho Chehab 
2159a0bf528SMauro Carvalho Chehab 	/* Wait for lock */
2169a0bf528SMauro Carvalho Chehab 	for (n = 0;  n < 20;  n++) {
2179a0bf528SMauro Carvalho Chehab 		val = lgs8gl5_read_reg(state, REG_STATUS);
2189a0bf528SMauro Carvalho Chehab 		dprintk("Wait for lock[%d] 0x%02X\n", n, val);
2199a0bf528SMauro Carvalho Chehab 		if (val & REG_STATUS_LOCK)
2209a0bf528SMauro Carvalho Chehab 			break;
2219a0bf528SMauro Carvalho Chehab 		msleep(12);
2229a0bf528SMauro Carvalho Chehab 	}
2239a0bf528SMauro Carvalho Chehab 	if (!(val & REG_STATUS_LOCK))
2249a0bf528SMauro Carvalho Chehab 		return;
2259a0bf528SMauro Carvalho Chehab 
2269a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
2279a0bf528SMauro Carvalho Chehab 	lgs8gl5_soft_reset(state);
2289a0bf528SMauro Carvalho Chehab }
2299a0bf528SMauro Carvalho Chehab 
2309a0bf528SMauro Carvalho Chehab 
2319a0bf528SMauro Carvalho Chehab static int
2329a0bf528SMauro Carvalho Chehab lgs8gl5_init(struct dvb_frontend *fe)
2339a0bf528SMauro Carvalho Chehab {
2349a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
2359a0bf528SMauro Carvalho Chehab 
2369a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
2379a0bf528SMauro Carvalho Chehab 
2389a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
2399a0bf528SMauro Carvalho Chehab 	lgs8gl5_soft_reset(state);
2409a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_07, 0x10);
2419a0bf528SMauro Carvalho Chehab 	lgs8gl5_update_reg(state, REG_07, 0x10);
2429a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_09, 0x0e);
2439a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0A, 0xe5);
2449a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0B, 0x35);
2459a0bf528SMauro Carvalho Chehab 	lgs8gl5_write_reg(state, REG_0C, 0x30);
2469a0bf528SMauro Carvalho Chehab 
2479a0bf528SMauro Carvalho Chehab 	return 0;
2489a0bf528SMauro Carvalho Chehab }
2499a0bf528SMauro Carvalho Chehab 
2509a0bf528SMauro Carvalho Chehab 
2519a0bf528SMauro Carvalho Chehab static int
2520df289a2SMauro Carvalho Chehab lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)
2539a0bf528SMauro Carvalho Chehab {
2549a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
2559a0bf528SMauro Carvalho Chehab 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2569a0bf528SMauro Carvalho Chehab 	u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
2579a0bf528SMauro Carvalho Chehab 
2589a0bf528SMauro Carvalho Chehab 	*status = 0;
2599a0bf528SMauro Carvalho Chehab 
2609a0bf528SMauro Carvalho Chehab 	if ((level & REG_STRENGTH_MASK) > 0)
2619a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
2629a0bf528SMauro Carvalho Chehab 	if (level & REG_STRENGTH_CARRIER)
2639a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
2649a0bf528SMauro Carvalho Chehab 	if (flags & REG_STATUS_SYNC)
2659a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
2669a0bf528SMauro Carvalho Chehab 	if (flags & REG_STATUS_LOCK)
2679a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
2689a0bf528SMauro Carvalho Chehab 
2699a0bf528SMauro Carvalho Chehab 	return 0;
2709a0bf528SMauro Carvalho Chehab }
2719a0bf528SMauro Carvalho Chehab 
2729a0bf528SMauro Carvalho Chehab 
2739a0bf528SMauro Carvalho Chehab static int
2749a0bf528SMauro Carvalho Chehab lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
2759a0bf528SMauro Carvalho Chehab {
2769a0bf528SMauro Carvalho Chehab 	*ber = 0;
2779a0bf528SMauro Carvalho Chehab 
2789a0bf528SMauro Carvalho Chehab 	return 0;
2799a0bf528SMauro Carvalho Chehab }
2809a0bf528SMauro Carvalho Chehab 
2819a0bf528SMauro Carvalho Chehab 
2829a0bf528SMauro Carvalho Chehab static int
2839a0bf528SMauro Carvalho Chehab lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
2849a0bf528SMauro Carvalho Chehab {
2859a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
2869a0bf528SMauro Carvalho Chehab 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2879a0bf528SMauro Carvalho Chehab 	*signal_strength = (level & REG_STRENGTH_MASK) << 8;
2889a0bf528SMauro Carvalho Chehab 
2899a0bf528SMauro Carvalho Chehab 	return 0;
2909a0bf528SMauro Carvalho Chehab }
2919a0bf528SMauro Carvalho Chehab 
2929a0bf528SMauro Carvalho Chehab 
2939a0bf528SMauro Carvalho Chehab static int
2949a0bf528SMauro Carvalho Chehab lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
2959a0bf528SMauro Carvalho Chehab {
2969a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
2979a0bf528SMauro Carvalho Chehab 	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2989a0bf528SMauro Carvalho Chehab 	*snr = (level & REG_STRENGTH_MASK) << 8;
2999a0bf528SMauro Carvalho Chehab 
3009a0bf528SMauro Carvalho Chehab 	return 0;
3019a0bf528SMauro Carvalho Chehab }
3029a0bf528SMauro Carvalho Chehab 
3039a0bf528SMauro Carvalho Chehab 
3049a0bf528SMauro Carvalho Chehab static int
3059a0bf528SMauro Carvalho Chehab lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
3069a0bf528SMauro Carvalho Chehab {
3079a0bf528SMauro Carvalho Chehab 	*ucblocks = 0;
3089a0bf528SMauro Carvalho Chehab 
3099a0bf528SMauro Carvalho Chehab 	return 0;
3109a0bf528SMauro Carvalho Chehab }
3119a0bf528SMauro Carvalho Chehab 
3129a0bf528SMauro Carvalho Chehab 
3139a0bf528SMauro Carvalho Chehab static int
3149a0bf528SMauro Carvalho Chehab lgs8gl5_set_frontend(struct dvb_frontend *fe)
3159a0bf528SMauro Carvalho Chehab {
3169a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
3179a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
3189a0bf528SMauro Carvalho Chehab 
3199a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
3209a0bf528SMauro Carvalho Chehab 
3219a0bf528SMauro Carvalho Chehab 	if (p->bandwidth_hz != 8000000)
3229a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3239a0bf528SMauro Carvalho Chehab 
3249a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
3259a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
3269a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
3279a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
3289a0bf528SMauro Carvalho Chehab 	}
3299a0bf528SMauro Carvalho Chehab 
3309a0bf528SMauro Carvalho Chehab 	/* lgs8gl5_set_inversion(state, p->inversion); */
3319a0bf528SMauro Carvalho Chehab 
3329a0bf528SMauro Carvalho Chehab 	lgs8gl5_start_demod(state);
3339a0bf528SMauro Carvalho Chehab 
3349a0bf528SMauro Carvalho Chehab 	return 0;
3359a0bf528SMauro Carvalho Chehab }
3369a0bf528SMauro Carvalho Chehab 
3379a0bf528SMauro Carvalho Chehab 
3389a0bf528SMauro Carvalho Chehab static int
3397e3e68bcSMauro Carvalho Chehab lgs8gl5_get_frontend(struct dvb_frontend *fe,
3407e3e68bcSMauro Carvalho Chehab 		     struct dtv_frontend_properties *p)
3419a0bf528SMauro Carvalho Chehab {
3429a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
3437e3e68bcSMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 	u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
3459a0bf528SMauro Carvalho Chehab 
3469a0bf528SMauro Carvalho Chehab 	p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
3479a0bf528SMauro Carvalho Chehab 
3489a0bf528SMauro Carvalho Chehab 	p->code_rate_HP = FEC_1_2;
3499a0bf528SMauro Carvalho Chehab 	p->code_rate_LP = FEC_7_8;
3509a0bf528SMauro Carvalho Chehab 	p->guard_interval = GUARD_INTERVAL_1_32;
3519a0bf528SMauro Carvalho Chehab 	p->transmission_mode = TRANSMISSION_MODE_2K;
3529a0bf528SMauro Carvalho Chehab 	p->modulation = QAM_64;
3539a0bf528SMauro Carvalho Chehab 	p->hierarchy = HIERARCHY_NONE;
3549a0bf528SMauro Carvalho Chehab 	p->bandwidth_hz = 8000000;
3559a0bf528SMauro Carvalho Chehab 
3569a0bf528SMauro Carvalho Chehab 	return 0;
3579a0bf528SMauro Carvalho Chehab }
3589a0bf528SMauro Carvalho Chehab 
3599a0bf528SMauro Carvalho Chehab 
3609a0bf528SMauro Carvalho Chehab static int
3619a0bf528SMauro Carvalho Chehab lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
3629a0bf528SMauro Carvalho Chehab 		struct dvb_frontend_tune_settings *fesettings)
3639a0bf528SMauro Carvalho Chehab {
3649a0bf528SMauro Carvalho Chehab 	fesettings->min_delay_ms = 240;
3659a0bf528SMauro Carvalho Chehab 	fesettings->step_size    = 0;
3669a0bf528SMauro Carvalho Chehab 	fesettings->max_drift    = 0;
3679a0bf528SMauro Carvalho Chehab 	return 0;
3689a0bf528SMauro Carvalho Chehab }
3699a0bf528SMauro Carvalho Chehab 
3709a0bf528SMauro Carvalho Chehab 
3719a0bf528SMauro Carvalho Chehab static void
3729a0bf528SMauro Carvalho Chehab lgs8gl5_release(struct dvb_frontend *fe)
3739a0bf528SMauro Carvalho Chehab {
3749a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = fe->demodulator_priv;
3759a0bf528SMauro Carvalho Chehab 	kfree(state);
3769a0bf528SMauro Carvalho Chehab }
3779a0bf528SMauro Carvalho Chehab 
3789a0bf528SMauro Carvalho Chehab 
379bd336e63SMax Kellermann static const struct dvb_frontend_ops lgs8gl5_ops;
3809a0bf528SMauro Carvalho Chehab 
3819a0bf528SMauro Carvalho Chehab 
3829a0bf528SMauro Carvalho Chehab struct dvb_frontend*
3839a0bf528SMauro Carvalho Chehab lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
3849a0bf528SMauro Carvalho Chehab {
3859a0bf528SMauro Carvalho Chehab 	struct lgs8gl5_state *state = NULL;
3869a0bf528SMauro Carvalho Chehab 
3879a0bf528SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
3889a0bf528SMauro Carvalho Chehab 
3899a0bf528SMauro Carvalho Chehab 	/* Allocate memory for the internal state */
3909a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
3919a0bf528SMauro Carvalho Chehab 	if (state == NULL)
3929a0bf528SMauro Carvalho Chehab 		goto error;
3939a0bf528SMauro Carvalho Chehab 
3949a0bf528SMauro Carvalho Chehab 	/* Setup the state */
3959a0bf528SMauro Carvalho Chehab 	state->config = config;
3969a0bf528SMauro Carvalho Chehab 	state->i2c    = i2c;
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab 	/* Check if the demod is there */
3999a0bf528SMauro Carvalho Chehab 	if (lgs8gl5_read_reg(state, REG_RESET) < 0)
4009a0bf528SMauro Carvalho Chehab 		goto error;
4019a0bf528SMauro Carvalho Chehab 
4029a0bf528SMauro Carvalho Chehab 	/* Create dvb_frontend */
4039a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &lgs8gl5_ops,
4049a0bf528SMauro Carvalho Chehab 		sizeof(struct dvb_frontend_ops));
4059a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
4069a0bf528SMauro Carvalho Chehab 	return &state->frontend;
4079a0bf528SMauro Carvalho Chehab 
4089a0bf528SMauro Carvalho Chehab error:
4099a0bf528SMauro Carvalho Chehab 	kfree(state);
4109a0bf528SMauro Carvalho Chehab 	return NULL;
4119a0bf528SMauro Carvalho Chehab }
4129a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(lgs8gl5_attach);
4139a0bf528SMauro Carvalho Chehab 
4149a0bf528SMauro Carvalho Chehab 
415bd336e63SMax Kellermann static const struct dvb_frontend_ops lgs8gl5_ops = {
4169a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_DTMB },
4179a0bf528SMauro Carvalho Chehab 	.info = {
4189a0bf528SMauro Carvalho Chehab 		.name			= "Legend Silicon LGS-8GL5 DMB-TH",
419f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	= 474 * MHz,
420f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 858 * MHz,
421f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	=  10 * kHz,
4229a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_AUTO |
4239a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
4249a0bf528SMauro Carvalho Chehab 			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
4259a0bf528SMauro Carvalho Chehab 			FE_CAN_TRANSMISSION_MODE_AUTO |
4269a0bf528SMauro Carvalho Chehab 			FE_CAN_BANDWIDTH_AUTO |
4279a0bf528SMauro Carvalho Chehab 			FE_CAN_GUARD_INTERVAL_AUTO |
4289a0bf528SMauro Carvalho Chehab 			FE_CAN_HIERARCHY_AUTO |
4299a0bf528SMauro Carvalho Chehab 			FE_CAN_RECOVER
4309a0bf528SMauro Carvalho Chehab 	},
4319a0bf528SMauro Carvalho Chehab 
4329a0bf528SMauro Carvalho Chehab 	.release = lgs8gl5_release,
4339a0bf528SMauro Carvalho Chehab 
4349a0bf528SMauro Carvalho Chehab 	.init = lgs8gl5_init,
4359a0bf528SMauro Carvalho Chehab 
4369a0bf528SMauro Carvalho Chehab 	.set_frontend = lgs8gl5_set_frontend,
4379a0bf528SMauro Carvalho Chehab 	.get_frontend = lgs8gl5_get_frontend,
4389a0bf528SMauro Carvalho Chehab 	.get_tune_settings = lgs8gl5_get_tune_settings,
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab 	.read_status = lgs8gl5_read_status,
4419a0bf528SMauro Carvalho Chehab 	.read_ber = lgs8gl5_read_ber,
4429a0bf528SMauro Carvalho Chehab 	.read_signal_strength = lgs8gl5_read_signal_strength,
4439a0bf528SMauro Carvalho Chehab 	.read_snr = lgs8gl5_read_snr,
4449a0bf528SMauro Carvalho Chehab 	.read_ucblocks = lgs8gl5_read_ucblocks,
4459a0bf528SMauro Carvalho Chehab };
4469a0bf528SMauro Carvalho Chehab 
4479a0bf528SMauro Carvalho Chehab 
4489a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
4499a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
4509a0bf528SMauro Carvalho Chehab 
4519a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
4529a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Timothy Lee");
4539a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
454