18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    Copyright (C) 2008 Sirius International (Hong Kong) Limited
68c2ecf20Sopenharmony_ci	Timothy Lee <timothy.lee@siriushk.com>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci*/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
178c2ecf20Sopenharmony_ci#include "lgs8gl5.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define REG_RESET		0x02
218c2ecf20Sopenharmony_ci#define REG_RESET_OFF			0x01
228c2ecf20Sopenharmony_ci#define REG_03			0x03
238c2ecf20Sopenharmony_ci#define REG_04			0x04
248c2ecf20Sopenharmony_ci#define REG_07			0x07
258c2ecf20Sopenharmony_ci#define REG_09			0x09
268c2ecf20Sopenharmony_ci#define REG_0A			0x0a
278c2ecf20Sopenharmony_ci#define REG_0B			0x0b
288c2ecf20Sopenharmony_ci#define REG_0C			0x0c
298c2ecf20Sopenharmony_ci#define REG_37			0x37
308c2ecf20Sopenharmony_ci#define REG_STRENGTH		0x4b
318c2ecf20Sopenharmony_ci#define REG_STRENGTH_MASK		0x7f
328c2ecf20Sopenharmony_ci#define REG_STRENGTH_CARRIER		0x80
338c2ecf20Sopenharmony_ci#define REG_INVERSION		0x7c
348c2ecf20Sopenharmony_ci#define REG_INVERSION_ON		0x80
358c2ecf20Sopenharmony_ci#define REG_7D			0x7d
368c2ecf20Sopenharmony_ci#define REG_7E			0x7e
378c2ecf20Sopenharmony_ci#define REG_A2			0xa2
388c2ecf20Sopenharmony_ci#define REG_STATUS		0xa4
398c2ecf20Sopenharmony_ci#define REG_STATUS_SYNC		0x04
408c2ecf20Sopenharmony_ci#define REG_STATUS_LOCK		0x01
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct lgs8gl5_state {
448c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
458c2ecf20Sopenharmony_ci	const struct lgs8gl5_config *config;
468c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int debug;
518c2ecf20Sopenharmony_ci#define dprintk(args...) \
528c2ecf20Sopenharmony_ci	do { \
538c2ecf20Sopenharmony_ci		if (debug) \
548c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "lgs8gl5: " args); \
558c2ecf20Sopenharmony_ci	} while (0)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Writes into demod's register */
598c2ecf20Sopenharmony_cistatic int
608c2ecf20Sopenharmony_cilgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int ret;
638c2ecf20Sopenharmony_ci	u8 buf[] = {reg, data};
648c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
658c2ecf20Sopenharmony_ci		.addr  = state->config->demod_address,
668c2ecf20Sopenharmony_ci		.flags = 0,
678c2ecf20Sopenharmony_ci		.buf   = buf,
688c2ecf20Sopenharmony_ci		.len   = 2
698c2ecf20Sopenharmony_ci	};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, &msg, 1);
728c2ecf20Sopenharmony_ci	if (ret != 1)
738c2ecf20Sopenharmony_ci		dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
748c2ecf20Sopenharmony_ci			__func__, reg, data, ret);
758c2ecf20Sopenharmony_ci	return (ret != 1) ? -1 : 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* Reads from demod's register */
808c2ecf20Sopenharmony_cistatic int
818c2ecf20Sopenharmony_cilgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int ret;
848c2ecf20Sopenharmony_ci	u8 b0[] = {reg};
858c2ecf20Sopenharmony_ci	u8 b1[] = {0};
868c2ecf20Sopenharmony_ci	struct i2c_msg msg[2] = {
878c2ecf20Sopenharmony_ci		{
888c2ecf20Sopenharmony_ci			.addr  = state->config->demod_address,
898c2ecf20Sopenharmony_ci			.flags = 0,
908c2ecf20Sopenharmony_ci			.buf   = b0,
918c2ecf20Sopenharmony_ci			.len   = 1
928c2ecf20Sopenharmony_ci		},
938c2ecf20Sopenharmony_ci		{
948c2ecf20Sopenharmony_ci			.addr  = state->config->demod_address,
958c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
968c2ecf20Sopenharmony_ci			.buf   = b1,
978c2ecf20Sopenharmony_ci			.len   = 1
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci	};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 2);
1028c2ecf20Sopenharmony_ci	if (ret != 2)
1038c2ecf20Sopenharmony_ci		return -EIO;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return b1[0];
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int
1108c2ecf20Sopenharmony_cilgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	lgs8gl5_read_reg(state, reg);
1138c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, reg, data);
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Writes into alternate device's register */
1198c2ecf20Sopenharmony_ci/* TODO:  Find out what that device is for! */
1208c2ecf20Sopenharmony_cistatic int
1218c2ecf20Sopenharmony_cilgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci	u8 b0[] = {reg};
1258c2ecf20Sopenharmony_ci	u8 b1[] = {0};
1268c2ecf20Sopenharmony_ci	u8 b2[] = {reg, data};
1278c2ecf20Sopenharmony_ci	struct i2c_msg msg[3] = {
1288c2ecf20Sopenharmony_ci		{
1298c2ecf20Sopenharmony_ci			.addr  = state->config->demod_address + 2,
1308c2ecf20Sopenharmony_ci			.flags = 0,
1318c2ecf20Sopenharmony_ci			.buf   = b0,
1328c2ecf20Sopenharmony_ci			.len   = 1
1338c2ecf20Sopenharmony_ci		},
1348c2ecf20Sopenharmony_ci		{
1358c2ecf20Sopenharmony_ci			.addr  = state->config->demod_address + 2,
1368c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
1378c2ecf20Sopenharmony_ci			.buf   = b1,
1388c2ecf20Sopenharmony_ci			.len   = 1
1398c2ecf20Sopenharmony_ci		},
1408c2ecf20Sopenharmony_ci		{
1418c2ecf20Sopenharmony_ci			.addr  = state->config->demod_address + 2,
1428c2ecf20Sopenharmony_ci			.flags = 0,
1438c2ecf20Sopenharmony_ci			.buf   = b2,
1448c2ecf20Sopenharmony_ci			.len   = 2
1458c2ecf20Sopenharmony_ci		},
1468c2ecf20Sopenharmony_ci	};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 3);
1498c2ecf20Sopenharmony_ci	return (ret != 3) ? -1 : 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic void
1548c2ecf20Sopenharmony_cilgs8gl5_soft_reset(struct lgs8gl5_state *state)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	u8 val;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	val = lgs8gl5_read_reg(state, REG_RESET);
1618c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
1628c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
1638c2ecf20Sopenharmony_ci	msleep(5);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Starts demodulation */
1688c2ecf20Sopenharmony_cistatic void
1698c2ecf20Sopenharmony_cilgs8gl5_start_demod(struct lgs8gl5_state *state)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	u8  val;
1728c2ecf20Sopenharmony_ci	int n;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
1778c2ecf20Sopenharmony_ci	lgs8gl5_soft_reset(state);
1788c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_07, 0x10);
1798c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_07, 0x10);
1808c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_09, 0x0e);
1818c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0A, 0xe5);
1828c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0B, 0x35);
1838c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0C, 0x30);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_03, 0x00);
1868c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_7E, 0x01);
1878c2ecf20Sopenharmony_ci	lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
1888c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_04, 0x02);
1898c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_37, 0x01);
1908c2ecf20Sopenharmony_ci	lgs8gl5_soft_reset(state);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Wait for carrier */
1938c2ecf20Sopenharmony_ci	for (n = 0;  n < 10;  n++) {
1948c2ecf20Sopenharmony_ci		val = lgs8gl5_read_reg(state, REG_STRENGTH);
1958c2ecf20Sopenharmony_ci		dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
1968c2ecf20Sopenharmony_ci		if (val & REG_STRENGTH_CARRIER)
1978c2ecf20Sopenharmony_ci			break;
1988c2ecf20Sopenharmony_ci		msleep(4);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	if (!(val & REG_STRENGTH_CARRIER))
2018c2ecf20Sopenharmony_ci		return;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* Wait for lock */
2048c2ecf20Sopenharmony_ci	for (n = 0;  n < 20;  n++) {
2058c2ecf20Sopenharmony_ci		val = lgs8gl5_read_reg(state, REG_STATUS);
2068c2ecf20Sopenharmony_ci		dprintk("Wait for lock[%d] 0x%02X\n", n, val);
2078c2ecf20Sopenharmony_ci		if (val & REG_STATUS_LOCK)
2088c2ecf20Sopenharmony_ci			break;
2098c2ecf20Sopenharmony_ci		msleep(12);
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	if (!(val & REG_STATUS_LOCK))
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
2158c2ecf20Sopenharmony_ci	lgs8gl5_soft_reset(state);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int
2208c2ecf20Sopenharmony_cilgs8gl5_init(struct dvb_frontend *fe)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
2278c2ecf20Sopenharmony_ci	lgs8gl5_soft_reset(state);
2288c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_07, 0x10);
2298c2ecf20Sopenharmony_ci	lgs8gl5_update_reg(state, REG_07, 0x10);
2308c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_09, 0x0e);
2318c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0A, 0xe5);
2328c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0B, 0x35);
2338c2ecf20Sopenharmony_ci	lgs8gl5_write_reg(state, REG_0C, 0x30);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int
2408c2ecf20Sopenharmony_cilgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
2438c2ecf20Sopenharmony_ci	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2448c2ecf20Sopenharmony_ci	u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	*status = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if ((level & REG_STRENGTH_MASK) > 0)
2498c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL;
2508c2ecf20Sopenharmony_ci	if (level & REG_STRENGTH_CARRIER)
2518c2ecf20Sopenharmony_ci		*status |= FE_HAS_CARRIER;
2528c2ecf20Sopenharmony_ci	if (flags & REG_STATUS_SYNC)
2538c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC;
2548c2ecf20Sopenharmony_ci	if (flags & REG_STATUS_LOCK)
2558c2ecf20Sopenharmony_ci		*status |= FE_HAS_LOCK;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int
2628c2ecf20Sopenharmony_cilgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	*ber = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int
2718c2ecf20Sopenharmony_cilgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
2748c2ecf20Sopenharmony_ci	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2758c2ecf20Sopenharmony_ci	*signal_strength = (level & REG_STRENGTH_MASK) << 8;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int
2828c2ecf20Sopenharmony_cilgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
2858c2ecf20Sopenharmony_ci	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
2868c2ecf20Sopenharmony_ci	*snr = (level & REG_STRENGTH_MASK) << 8;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int
2938c2ecf20Sopenharmony_cilgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	*ucblocks = 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int
3028c2ecf20Sopenharmony_cilgs8gl5_set_frontend(struct dvb_frontend *fe)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
3058c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (p->bandwidth_hz != 8000000)
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
3138c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
3148c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl)
3158c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 0);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* lgs8gl5_set_inversion(state, p->inversion); */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	lgs8gl5_start_demod(state);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int
3278c2ecf20Sopenharmony_cilgs8gl5_get_frontend(struct dvb_frontend *fe,
3288c2ecf20Sopenharmony_ci		     struct dtv_frontend_properties *p)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	p->code_rate_HP = FEC_1_2;
3378c2ecf20Sopenharmony_ci	p->code_rate_LP = FEC_7_8;
3388c2ecf20Sopenharmony_ci	p->guard_interval = GUARD_INTERVAL_1_32;
3398c2ecf20Sopenharmony_ci	p->transmission_mode = TRANSMISSION_MODE_2K;
3408c2ecf20Sopenharmony_ci	p->modulation = QAM_64;
3418c2ecf20Sopenharmony_ci	p->hierarchy = HIERARCHY_NONE;
3428c2ecf20Sopenharmony_ci	p->bandwidth_hz = 8000000;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int
3498c2ecf20Sopenharmony_cilgs8gl5_get_tune_settings(struct dvb_frontend *fe,
3508c2ecf20Sopenharmony_ci		struct dvb_frontend_tune_settings *fesettings)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	fesettings->min_delay_ms = 240;
3538c2ecf20Sopenharmony_ci	fesettings->step_size    = 0;
3548c2ecf20Sopenharmony_ci	fesettings->max_drift    = 0;
3558c2ecf20Sopenharmony_ci	return 0;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void
3608c2ecf20Sopenharmony_cilgs8gl5_release(struct dvb_frontend *fe)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = fe->demodulator_priv;
3638c2ecf20Sopenharmony_ci	kfree(state);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgs8gl5_ops;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistruct dvb_frontend*
3718c2ecf20Sopenharmony_cilgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct lgs8gl5_state *state = NULL;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* Allocate memory for the internal state */
3788c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
3798c2ecf20Sopenharmony_ci	if (state == NULL)
3808c2ecf20Sopenharmony_ci		goto error;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* Setup the state */
3838c2ecf20Sopenharmony_ci	state->config = config;
3848c2ecf20Sopenharmony_ci	state->i2c    = i2c;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Check if the demod is there */
3878c2ecf20Sopenharmony_ci	if (lgs8gl5_read_reg(state, REG_RESET) < 0)
3888c2ecf20Sopenharmony_ci		goto error;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Create dvb_frontend */
3918c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &lgs8gl5_ops,
3928c2ecf20Sopenharmony_ci		sizeof(struct dvb_frontend_ops));
3938c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
3948c2ecf20Sopenharmony_ci	return &state->frontend;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cierror:
3978c2ecf20Sopenharmony_ci	kfree(state);
3988c2ecf20Sopenharmony_ci	return NULL;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lgs8gl5_attach);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgs8gl5_ops = {
4048c2ecf20Sopenharmony_ci	.delsys = { SYS_DTMB },
4058c2ecf20Sopenharmony_ci	.info = {
4068c2ecf20Sopenharmony_ci		.name			= "Legend Silicon LGS-8GL5 DMB-TH",
4078c2ecf20Sopenharmony_ci		.frequency_min_hz	= 474 * MHz,
4088c2ecf20Sopenharmony_ci		.frequency_max_hz	= 858 * MHz,
4098c2ecf20Sopenharmony_ci		.frequency_stepsize_hz	=  10 * kHz,
4108c2ecf20Sopenharmony_ci		.caps = FE_CAN_FEC_AUTO |
4118c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
4128c2ecf20Sopenharmony_ci			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
4138c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
4148c2ecf20Sopenharmony_ci			FE_CAN_BANDWIDTH_AUTO |
4158c2ecf20Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO |
4168c2ecf20Sopenharmony_ci			FE_CAN_HIERARCHY_AUTO |
4178c2ecf20Sopenharmony_ci			FE_CAN_RECOVER
4188c2ecf20Sopenharmony_ci	},
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	.release = lgs8gl5_release,
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	.init = lgs8gl5_init,
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	.set_frontend = lgs8gl5_set_frontend,
4258c2ecf20Sopenharmony_ci	.get_frontend = lgs8gl5_get_frontend,
4268c2ecf20Sopenharmony_ci	.get_tune_settings = lgs8gl5_get_tune_settings,
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	.read_status = lgs8gl5_read_status,
4298c2ecf20Sopenharmony_ci	.read_ber = lgs8gl5_read_ber,
4308c2ecf20Sopenharmony_ci	.read_signal_strength = lgs8gl5_read_signal_strength,
4318c2ecf20Sopenharmony_ci	.read_snr = lgs8gl5_read_snr,
4328c2ecf20Sopenharmony_ci	.read_ucblocks = lgs8gl5_read_ucblocks,
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
4378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
4408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Timothy Lee");
4418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
442