18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
48c2ecf20Sopenharmony_ci *    LGS8913, LGS8GL5, LGS8G75
58c2ecf20Sopenharmony_ci *    experimental support LGS8G42, LGS8G52
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *    Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
88c2ecf20Sopenharmony_ci *    Copyright (C) 2008 Sirius International (Hong Kong) Limited
98c2ecf20Sopenharmony_ci *    Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/div64.h>
138c2ecf20Sopenharmony_ci#include <linux/firmware.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "lgs8gxx.h"
188c2ecf20Sopenharmony_ci#include "lgs8gxx_priv.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define dprintk(args...) \
218c2ecf20Sopenharmony_ci	do { \
228c2ecf20Sopenharmony_ci		if (debug) \
238c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "lgs8gxx: " args); \
248c2ecf20Sopenharmony_ci	} while (0)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int debug;
278c2ecf20Sopenharmony_cistatic int fake_signal_str = 1;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define LGS8GXX_FIRMWARE "lgs8g75.fw"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cimodule_param(fake_signal_str, int, 0644);
358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
368c2ecf20Sopenharmony_ci"Signal strength calculation is slow.(default:on).");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* LGS8GXX internal helper functions */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int ret;
438c2ecf20Sopenharmony_ci	u8 buf[] = { reg, data };
448c2ecf20Sopenharmony_ci	struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	msg.addr = priv->config->demod_address;
478c2ecf20Sopenharmony_ci	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
488c2ecf20Sopenharmony_ci		msg.addr += 0x02;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (debug >= 2)
518c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg, 1);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (ret != 1)
568c2ecf20Sopenharmony_ci		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
578c2ecf20Sopenharmony_ci			__func__, reg, data, ret);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return (ret != 1) ? -1 : 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int ret;
658c2ecf20Sopenharmony_ci	u8 dev_addr;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	u8 b0[] = { reg };
688c2ecf20Sopenharmony_ci	u8 b1[] = { 0 };
698c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
708c2ecf20Sopenharmony_ci		{ .flags = 0, .buf = b0, .len = 1 },
718c2ecf20Sopenharmony_ci		{ .flags = I2C_M_RD, .buf = b1, .len = 1 },
728c2ecf20Sopenharmony_ci	};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	dev_addr = priv->config->demod_address;
758c2ecf20Sopenharmony_ci	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
768c2ecf20Sopenharmony_ci		dev_addr += 0x02;
778c2ecf20Sopenharmony_ci	msg[1].addr =  msg[0].addr = dev_addr;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, msg, 2);
808c2ecf20Sopenharmony_ci	if (ret != 2) {
818c2ecf20Sopenharmony_ci		dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret);
828c2ecf20Sopenharmony_ci		return -1;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	*p_data = b1[0];
868c2ecf20Sopenharmony_ci	if (debug >= 2)
878c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]);
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x02, 0x00);
948c2ecf20Sopenharmony_ci	msleep(1);
958c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x02, 0x01);
968c2ecf20Sopenharmony_ci	msleep(100);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
1028c2ecf20Sopenharmony_ci	u8 val, u8 delay, u8 tries)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u8 t;
1058c2ecf20Sopenharmony_ci	int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i = 0; i < tries; i++) {
1088c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, reg, &t);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		if ((t & mask) == val)
1118c2ecf20Sopenharmony_ci			return 0;
1128c2ecf20Sopenharmony_ci		msleep(delay);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 1;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	const struct lgs8gxx_config *config = priv->config;
1218c2ecf20Sopenharmony_ci	u8 if_conf;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if_conf = 0x10; /* AGC output on, RF_AGC output off; */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if_conf |=
1268c2ecf20Sopenharmony_ci		((config->ext_adc) ? 0x80 : 0x00) |
1278c2ecf20Sopenharmony_ci		((config->if_neg_center) ? 0x04 : 0x00) |
1288c2ecf20Sopenharmony_ci		((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
1298c2ecf20Sopenharmony_ci		((config->adc_signed) ? 0x02 : 0x00) |
1308c2ecf20Sopenharmony_ci		((config->if_neg_edge) ? 0x01 : 0x00);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (config->ext_adc &&
1338c2ecf20Sopenharmony_ci		(config->prod == LGS8GXX_PROD_LGS8G52)) {
1348c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xBA, 0x40);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x07, if_conf);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	u64 val;
1458c2ecf20Sopenharmony_ci	u32 v32;
1468c2ecf20Sopenharmony_ci	u32 if_clk;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if_clk = priv->config->if_clk_freq;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	val = freq;
1518c2ecf20Sopenharmony_ci	if (freq != 0) {
1528c2ecf20Sopenharmony_ci		val <<= 32;
1538c2ecf20Sopenharmony_ci		if (if_clk != 0)
1548c2ecf20Sopenharmony_ci			do_div(val, if_clk);
1558c2ecf20Sopenharmony_ci		v32 = val & 0xFFFFFFFF;
1568c2ecf20Sopenharmony_ci		dprintk("Set IF Freq to %dkHz\n", freq);
1578c2ecf20Sopenharmony_ci	} else {
1588c2ecf20Sopenharmony_ci		v32 = 0;
1598c2ecf20Sopenharmony_ci		dprintk("Set IF Freq to baseband\n");
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
1648c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
1658c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
1668c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
1678c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
1688c2ecf20Sopenharmony_ci	} else {
1698c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
1708c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
1718c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
1728c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	u64 val;
1818c2ecf20Sopenharmony_ci	u32 v32 = 0;
1828c2ecf20Sopenharmony_ci	u8 reg_addr, t;
1838c2ecf20Sopenharmony_ci	int i;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
1868c2ecf20Sopenharmony_ci		reg_addr = 0x23;
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		reg_addr = 0x48;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
1918c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, reg_addr, &t);
1928c2ecf20Sopenharmony_ci		v32 <<= 8;
1938c2ecf20Sopenharmony_ci		v32 |= t;
1948c2ecf20Sopenharmony_ci		reg_addr--;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	val = v32;
1988c2ecf20Sopenharmony_ci	val *= priv->config->if_clk_freq;
1998c2ecf20Sopenharmony_ci	val >>= 32;
2008c2ecf20Sopenharmony_ci	dprintk("AFC = %u kHz\n", (u32)val);
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	u8 t;
2078c2ecf20Sopenharmony_ci	u8 prod = priv->config->prod;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (prod == LGS8GXX_PROD_LGS8913)
2108c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC6, 0x01);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (prod == LGS8GXX_PROD_LGS8G75) {
2138c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x0C, &t);
2148c2ecf20Sopenharmony_ci		t &= (~0x04);
2158c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
2168c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x39, 0x00);
2178c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x3D, 0x04);
2188c2ecf20Sopenharmony_ci	} else if (prod == LGS8GXX_PROD_LGS8913 ||
2198c2ecf20Sopenharmony_ci		prod == LGS8GXX_PROD_LGS8GL5 ||
2208c2ecf20Sopenharmony_ci		prod == LGS8GXX_PROD_LGS8G42 ||
2218c2ecf20Sopenharmony_ci		prod == LGS8GXX_PROD_LGS8G52 ||
2228c2ecf20Sopenharmony_ci		prod == LGS8GXX_PROD_LGS8G54) {
2238c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x7E, &t);
2248c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* clear FEC self reset */
2278c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0xC5, &t);
2288c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (prod == LGS8GXX_PROD_LGS8913) {
2328c2ecf20Sopenharmony_ci		/* FEC auto detect */
2338c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC1, 0x03);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x7C, &t);
2368c2ecf20Sopenharmony_ci		t = (t & 0x8C) | 0x03;
2378c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x7C, t);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		/* BER test mode */
2408c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0xC3, &t);
2418c2ecf20Sopenharmony_ci		t = (t & 0xEF) |  0x10;
2428c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC3, t);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
2468c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xD9, 0x40);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	u8 t;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
2568c2ecf20Sopenharmony_ci		u8 t2;
2578c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x0C, &t);
2588c2ecf20Sopenharmony_ci		t &= (~0x80);
2598c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0C, t);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x0C, &t);
2628c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x19, &t2);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		if (((t&0x03) == 0x01) && (t2&0x01)) {
2658c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x6E, 0x05);
2668c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x39, 0x02);
2678c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x39, 0x03);
2688c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x3D, 0x05);
2698c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x3E, 0x28);
2708c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x53, 0x80);
2718c2ecf20Sopenharmony_ci		} else {
2728c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x6E, 0x3F);
2738c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x39, 0x00);
2748c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x3D, 0x04);
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		lgs8gxx_soft_reset(priv);
2788c2ecf20Sopenharmony_ci		return 0;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* turn off auto-detect; manual settings */
2828c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x7E, 0);
2838c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
2848c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC1, 0);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0xC5, &t);
2878c2ecf20Sopenharmony_ci	t = (t & 0xE0) | 0x06;
2888c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0xC5, t);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	lgs8gxx_soft_reset(priv);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int ret = 0;
2988c2ecf20Sopenharmony_ci	u8 t;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
3018c2ecf20Sopenharmony_ci		ret = lgs8gxx_read_reg(priv, 0x13, &t);
3028c2ecf20Sopenharmony_ci	else
3038c2ecf20Sopenharmony_ci		ret = lgs8gxx_read_reg(priv, 0x4B, &t);
3048c2ecf20Sopenharmony_ci	if (ret != 0)
3058c2ecf20Sopenharmony_ci		return ret;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
3088c2ecf20Sopenharmony_ci		*locked = ((t & 0x80) == 0x80) ? 1 : 0;
3098c2ecf20Sopenharmony_ci	else
3108c2ecf20Sopenharmony_ci		*locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/* Wait for Code Acquisition Lock */
3158c2ecf20Sopenharmony_cistatic int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int ret = 0;
3188c2ecf20Sopenharmony_ci	u8 reg, mask, val;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
3218c2ecf20Sopenharmony_ci		reg = 0x13;
3228c2ecf20Sopenharmony_ci		mask = 0x80;
3238c2ecf20Sopenharmony_ci		val = 0x80;
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		reg = 0x4B;
3268c2ecf20Sopenharmony_ci		mask = 0xC0;
3278c2ecf20Sopenharmony_ci		val = 0xC0;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
3318c2ecf20Sopenharmony_ci	*locked = (ret == 0) ? 1 : 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
3378c2ecf20Sopenharmony_ci					  u8 *finished)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	int ret = 0;
3408c2ecf20Sopenharmony_ci	u8 reg, mask, val;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
3438c2ecf20Sopenharmony_ci		reg = 0x1f;
3448c2ecf20Sopenharmony_ci		mask = 0xC0;
3458c2ecf20Sopenharmony_ci		val = 0x80;
3468c2ecf20Sopenharmony_ci	} else {
3478c2ecf20Sopenharmony_ci		reg = 0xA4;
3488c2ecf20Sopenharmony_ci		mask = 0x03;
3498c2ecf20Sopenharmony_ci		val = 0x01;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
3538c2ecf20Sopenharmony_ci	*finished = (ret == 0) ? 1 : 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return 0;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
3598c2ecf20Sopenharmony_ci	u8 *locked)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int err = 0;
3628c2ecf20Sopenharmony_ci	u8 ad_fini = 0;
3638c2ecf20Sopenharmony_ci	u8 t1, t2;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (gi == GI_945)
3668c2ecf20Sopenharmony_ci		dprintk("try GI 945\n");
3678c2ecf20Sopenharmony_ci	else if (gi == GI_595)
3688c2ecf20Sopenharmony_ci		dprintk("try GI 595\n");
3698c2ecf20Sopenharmony_ci	else if (gi == GI_420)
3708c2ecf20Sopenharmony_ci		dprintk("try GI 420\n");
3718c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
3728c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x0C, &t1);
3738c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x18, &t2);
3748c2ecf20Sopenharmony_ci		t1 &= ~(GI_MASK);
3758c2ecf20Sopenharmony_ci		t1 |= gi;
3768c2ecf20Sopenharmony_ci		t2 &= 0xFE;
3778c2ecf20Sopenharmony_ci		t2 |= cpn ? 0x01 : 0x00;
3788c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x0C, t1);
3798c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x18, t2);
3808c2ecf20Sopenharmony_ci	} else {
3818c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x04, gi);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	lgs8gxx_soft_reset(priv);
3848c2ecf20Sopenharmony_ci	err = lgs8gxx_wait_ca_lock(priv, locked);
3858c2ecf20Sopenharmony_ci	if (err || !(*locked))
3868c2ecf20Sopenharmony_ci		return err;
3878c2ecf20Sopenharmony_ci	err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
3888c2ecf20Sopenharmony_ci	if (err != 0)
3898c2ecf20Sopenharmony_ci		return err;
3908c2ecf20Sopenharmony_ci	if (ad_fini) {
3918c2ecf20Sopenharmony_ci		dprintk("auto detect finished\n");
3928c2ecf20Sopenharmony_ci	} else
3938c2ecf20Sopenharmony_ci		*locked = 0;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
3998c2ecf20Sopenharmony_ci			       u8 *detected_param, u8 *gi)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	int i, j;
4028c2ecf20Sopenharmony_ci	int err = 0;
4038c2ecf20Sopenharmony_ci	u8 locked = 0, tmp_gi;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	lgs8gxx_set_mode_auto(priv);
4088c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
4098c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x67, 0xAA);
4108c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x6E, 0x3F);
4118c2ecf20Sopenharmony_ci	} else {
4128c2ecf20Sopenharmony_ci		/* Guard Interval */
4138c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x03, 00);
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
4178c2ecf20Sopenharmony_ci		for (j = 0; j < 2; j++) {
4188c2ecf20Sopenharmony_ci			tmp_gi = GI_945;
4198c2ecf20Sopenharmony_ci			err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
4208c2ecf20Sopenharmony_ci			if (err)
4218c2ecf20Sopenharmony_ci				goto out;
4228c2ecf20Sopenharmony_ci			if (locked)
4238c2ecf20Sopenharmony_ci				goto locked;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci		for (j = 0; j < 2; j++) {
4268c2ecf20Sopenharmony_ci			tmp_gi = GI_420;
4278c2ecf20Sopenharmony_ci			err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
4288c2ecf20Sopenharmony_ci			if (err)
4298c2ecf20Sopenharmony_ci				goto out;
4308c2ecf20Sopenharmony_ci			if (locked)
4318c2ecf20Sopenharmony_ci				goto locked;
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci		tmp_gi = GI_595;
4348c2ecf20Sopenharmony_ci		err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
4358c2ecf20Sopenharmony_ci		if (err)
4368c2ecf20Sopenharmony_ci			goto out;
4378c2ecf20Sopenharmony_ci		if (locked)
4388c2ecf20Sopenharmony_ci			goto locked;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cilocked:
4428c2ecf20Sopenharmony_ci	if ((err == 0) && (locked == 1)) {
4438c2ecf20Sopenharmony_ci		u8 t;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
4468c2ecf20Sopenharmony_ci			lgs8gxx_read_reg(priv, 0xA2, &t);
4478c2ecf20Sopenharmony_ci			*detected_param = t;
4488c2ecf20Sopenharmony_ci		} else {
4498c2ecf20Sopenharmony_ci			lgs8gxx_read_reg(priv, 0x1F, &t);
4508c2ecf20Sopenharmony_ci			*detected_param = t & 0x3F;
4518c2ecf20Sopenharmony_ci		}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (tmp_gi == GI_945)
4548c2ecf20Sopenharmony_ci			dprintk("GI 945 locked\n");
4558c2ecf20Sopenharmony_ci		else if (tmp_gi == GI_595)
4568c2ecf20Sopenharmony_ci			dprintk("GI 595 locked\n");
4578c2ecf20Sopenharmony_ci		else if (tmp_gi == GI_420)
4588c2ecf20Sopenharmony_ci			dprintk("GI 420 locked\n");
4598c2ecf20Sopenharmony_ci		*gi = tmp_gi;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	if (!locked)
4628c2ecf20Sopenharmony_ci		err = -1;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciout:
4658c2ecf20Sopenharmony_ci	return err;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	s8 err;
4718c2ecf20Sopenharmony_ci	u8 gi = 0x2;
4728c2ecf20Sopenharmony_ci	u8 detected_param = 0;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (err != 0) {
4778c2ecf20Sopenharmony_ci		dprintk("lgs8gxx_auto_detect failed\n");
4788c2ecf20Sopenharmony_ci	} else
4798c2ecf20Sopenharmony_ci		dprintk("detected param = 0x%02X\n", detected_param);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* Apply detected parameters */
4828c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
4838c2ecf20Sopenharmony_ci		u8 inter_leave_len = detected_param & TIM_MASK ;
4848c2ecf20Sopenharmony_ci		/* Fix 8913 time interleaver detection bug */
4858c2ecf20Sopenharmony_ci		inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
4868c2ecf20Sopenharmony_ci		detected_param &= CF_MASK | SC_MASK  | LGS_FEC_MASK;
4878c2ecf20Sopenharmony_ci		detected_param |= inter_leave_len;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
4908c2ecf20Sopenharmony_ci		u8 t;
4918c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x19, &t);
4928c2ecf20Sopenharmony_ci		t &= 0x81;
4938c2ecf20Sopenharmony_ci		t |= detected_param << 1;
4948c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x19, t);
4958c2ecf20Sopenharmony_ci	} else {
4968c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x7D, detected_param);
4978c2ecf20Sopenharmony_ci		if (priv->config->prod == LGS8GXX_PROD_LGS8913)
4988c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0xC0, detected_param);
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci	/* lgs8gxx_soft_reset(priv); */
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* Enter manual mode */
5038c2ecf20Sopenharmony_ci	lgs8gxx_set_mode_manual(priv);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	switch (gi) {
5068c2ecf20Sopenharmony_ci	case GI_945:
5078c2ecf20Sopenharmony_ci		priv->curr_gi = 945; break;
5088c2ecf20Sopenharmony_ci	case GI_595:
5098c2ecf20Sopenharmony_ci		priv->curr_gi = 595; break;
5108c2ecf20Sopenharmony_ci	case GI_420:
5118c2ecf20Sopenharmony_ci		priv->curr_gi = 420; break;
5128c2ecf20Sopenharmony_ci	default:
5138c2ecf20Sopenharmony_ci		priv->curr_gi = 945; break;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
5188c2ecf20Sopenharmony_ci	u8 serial, u8 clk_pol, u8 clk_gated)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	int ret = 0;
5218c2ecf20Sopenharmony_ci	u8 t, reg_addr;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
5248c2ecf20Sopenharmony_ci	ret = lgs8gxx_read_reg(priv, reg_addr, &t);
5258c2ecf20Sopenharmony_ci	if (ret != 0)
5268c2ecf20Sopenharmony_ci		return ret;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	t &= 0xF8;
5298c2ecf20Sopenharmony_ci	t |= serial ? TS_SERIAL : TS_PARALLEL;
5308c2ecf20Sopenharmony_ci	t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
5318c2ecf20Sopenharmony_ci	t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	ret = lgs8gxx_write_reg(priv, reg_addr, t);
5348c2ecf20Sopenharmony_ci	if (ret != 0)
5358c2ecf20Sopenharmony_ci		return ret;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	return 0;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/* A/D input peak-to-peak voltage range */
5418c2ecf20Sopenharmony_cistatic int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
5428c2ecf20Sopenharmony_ci	u8 sel)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	u8 r26 = 0x73, r27 = 0x90;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
5478c2ecf20Sopenharmony_ci		return 0;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	r26 |= (sel & 0x01) << 7;
5508c2ecf20Sopenharmony_ci	r27 |= (sel & 0x02) >> 1;
5518c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x26, r26);
5528c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x27, r27);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	return 0;
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci/* LGS8913 demod frontend functions */
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int lgs8913_init(struct lgs8gxx_state *priv)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	u8 t;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* LGS8913 specific */
5648c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0xc1, 0x3);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0x7c, &t);
5678c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* LGS8913 specific */
5708c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0xc3, &t);
5718c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0xc3, t&0x10);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	return 0;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic int lgs8g75_init_data(struct lgs8gxx_state *priv)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	const struct firmware *fw;
5808c2ecf20Sopenharmony_ci	int rc;
5818c2ecf20Sopenharmony_ci	int i;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
5848c2ecf20Sopenharmony_ci	if (rc)
5858c2ecf20Sopenharmony_ci		return rc;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0xC6, 0x40);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x3D, 0x04);
5908c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x39, 0x00);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x3A, 0x00);
5938c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x38, 0x00);
5948c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x3B, 0x00);
5958c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x38, 0x00);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	for (i = 0; i < fw->size; i++) {
5988c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x38, 0x00);
5998c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
6008c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
6018c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x3C, fw->data[i]);
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	lgs8gxx_write_reg(priv, 0x38, 0x00);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	release_firmware(fw);
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int lgs8gxx_init(struct dvb_frontend *fe)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv =
6138c2ecf20Sopenharmony_ci		(struct lgs8gxx_state *)fe->demodulator_priv;
6148c2ecf20Sopenharmony_ci	const struct lgs8gxx_config *config = priv->config;
6158c2ecf20Sopenharmony_ci	u8 data = 0;
6168c2ecf20Sopenharmony_ci	s8 err;
6178c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0, &data);
6208c2ecf20Sopenharmony_ci	dprintk("reg 0 = 0x%02X\n", data);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (config->prod == LGS8GXX_PROD_LGS8G75)
6238c2ecf20Sopenharmony_ci		lgs8g75_set_adc_vpp(priv, config->adc_vpp);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* Setup MPEG output format */
6268c2ecf20Sopenharmony_ci	err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
6278c2ecf20Sopenharmony_ci				    config->ts_clk_pol,
6288c2ecf20Sopenharmony_ci				    config->ts_clk_gated);
6298c2ecf20Sopenharmony_ci	if (err != 0)
6308c2ecf20Sopenharmony_ci		return -EIO;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (config->prod == LGS8GXX_PROD_LGS8913)
6338c2ecf20Sopenharmony_ci		lgs8913_init(priv);
6348c2ecf20Sopenharmony_ci	lgs8gxx_set_if_freq(priv, priv->config->if_freq);
6358c2ecf20Sopenharmony_ci	lgs8gxx_set_ad_mode(priv);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	return 0;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic void lgs8gxx_release(struct dvb_frontend *fe)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	struct lgs8gxx_state *state = fe->demodulator_priv;
6438c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	kfree(state);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (len != 2)
6548c2ecf20Sopenharmony_ci		return -EINVAL;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	return lgs8gxx_write_reg(priv, buf[0], buf[1]);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int lgs8gxx_set_fe(struct dvb_frontend *fe)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
6628c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* set frequency */
6678c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
6688c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
6698c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl)
6708c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 0);
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* start auto lock */
6748c2ecf20Sopenharmony_ci	lgs8gxx_auto_lock(priv);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	msleep(10);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* TODO: get real readings from device */
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* bandwidth */
6818c2ecf20Sopenharmony_ci	fe_params->bandwidth_hz = 8000000;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	fe_params->code_rate_HP = FEC_AUTO;
6848c2ecf20Sopenharmony_ci	fe_params->code_rate_LP = FEC_AUTO;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	fe_params->modulation = QAM_AUTO;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* transmission mode */
6898c2ecf20Sopenharmony_ci	fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* guard interval */
6928c2ecf20Sopenharmony_ci	fe_params->guard_interval = GUARD_INTERVAL_AUTO;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	/* hierarchy */
6958c2ecf20Sopenharmony_ci	fe_params->hierarchy = HIERARCHY_NONE;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return 0;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic
7018c2ecf20Sopenharmony_ciint lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
7028c2ecf20Sopenharmony_ci			      struct dvb_frontend_tune_settings *fesettings)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	/* FIXME: copy from tda1004x.c */
7058c2ecf20Sopenharmony_ci	fesettings->min_delay_ms = 800;
7068c2ecf20Sopenharmony_ci	fesettings->step_size = 0;
7078c2ecf20Sopenharmony_ci	fesettings->max_drift = 0;
7088c2ecf20Sopenharmony_ci	return 0;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int lgs8gxx_read_status(struct dvb_frontend *fe,
7128c2ecf20Sopenharmony_ci			       enum fe_status *fe_status)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
7158c2ecf20Sopenharmony_ci	s8 ret;
7168c2ecf20Sopenharmony_ci	u8 t, locked = 0;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
7198c2ecf20Sopenharmony_ci	*fe_status = 0;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	lgs8gxx_get_afc_phase(priv);
7228c2ecf20Sopenharmony_ci	lgs8gxx_is_locked(priv, &locked);
7238c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
7248c2ecf20Sopenharmony_ci		if (locked)
7258c2ecf20Sopenharmony_ci			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
7268c2ecf20Sopenharmony_ci				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
7278c2ecf20Sopenharmony_ci		return 0;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
7318c2ecf20Sopenharmony_ci	if (ret != 0)
7328c2ecf20Sopenharmony_ci		return -EIO;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	dprintk("Reg 0x4B: 0x%02X\n", t);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	*fe_status = 0;
7378c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
7388c2ecf20Sopenharmony_ci		if ((t & 0x40) == 0x40)
7398c2ecf20Sopenharmony_ci			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
7408c2ecf20Sopenharmony_ci		if ((t & 0x80) == 0x80)
7418c2ecf20Sopenharmony_ci			*fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
7428c2ecf20Sopenharmony_ci				FE_HAS_LOCK;
7438c2ecf20Sopenharmony_ci	} else {
7448c2ecf20Sopenharmony_ci		if ((t & 0x80) == 0x80)
7458c2ecf20Sopenharmony_ci			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
7468c2ecf20Sopenharmony_ci				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/* success */
7508c2ecf20Sopenharmony_ci	dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
7518c2ecf20Sopenharmony_ci	return 0;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	u16 v;
7578c2ecf20Sopenharmony_ci	u8 agc_lvl[2], cat;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
7608c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
7618c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	v = agc_lvl[0];
7648c2ecf20Sopenharmony_ci	v <<= 8;
7658c2ecf20Sopenharmony_ci	v |= agc_lvl[1];
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	dprintk("agc_lvl: 0x%04X\n", v);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (v < 0x100)
7708c2ecf20Sopenharmony_ci		cat = 0;
7718c2ecf20Sopenharmony_ci	else if (v < 0x190)
7728c2ecf20Sopenharmony_ci		cat = 5;
7738c2ecf20Sopenharmony_ci	else if (v < 0x2A8)
7748c2ecf20Sopenharmony_ci		cat = 4;
7758c2ecf20Sopenharmony_ci	else if (v < 0x381)
7768c2ecf20Sopenharmony_ci		cat = 3;
7778c2ecf20Sopenharmony_ci	else if (v < 0x400)
7788c2ecf20Sopenharmony_ci		cat = 2;
7798c2ecf20Sopenharmony_ci	else if (v == 0x400)
7808c2ecf20Sopenharmony_ci		cat = 1;
7818c2ecf20Sopenharmony_ci	else
7828c2ecf20Sopenharmony_ci		cat = 0;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	*signal = cat * 65535 / 5;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	return 0;
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	u8 t; s8 ret;
7928c2ecf20Sopenharmony_ci	s16 max_strength = 0;
7938c2ecf20Sopenharmony_ci	u8 str;
7948c2ecf20Sopenharmony_ci	u16 i, gi = priv->curr_gi;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
7998c2ecf20Sopenharmony_ci	if (ret != 0)
8008c2ecf20Sopenharmony_ci		return -EIO;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (fake_signal_str) {
8038c2ecf20Sopenharmony_ci		if ((t & 0xC0) == 0xC0) {
8048c2ecf20Sopenharmony_ci			dprintk("Fake signal strength\n");
8058c2ecf20Sopenharmony_ci			*signal = 0x7FFF;
8068c2ecf20Sopenharmony_ci		} else
8078c2ecf20Sopenharmony_ci			*signal = 0;
8088c2ecf20Sopenharmony_ci		return 0;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	dprintk("gi = %d\n", gi);
8128c2ecf20Sopenharmony_ci	for (i = 0; i < gi; i++) {
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		if ((i & 0xFF) == 0)
8158c2ecf20Sopenharmony_ci			lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
8168c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x94, &str);
8198c2ecf20Sopenharmony_ci		if (max_strength < str)
8208c2ecf20Sopenharmony_ci			max_strength = str;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	*signal = max_strength;
8248c2ecf20Sopenharmony_ci	dprintk("%s: signal=0x%02X\n", __func__, *signal);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0x95, &t);
8278c2ecf20Sopenharmony_ci	dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	return 0;
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	u8 t;
8358c2ecf20Sopenharmony_ci	s16 v = 0;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0xB1, &t);
8408c2ecf20Sopenharmony_ci	v |= t;
8418c2ecf20Sopenharmony_ci	v <<= 8;
8428c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 0xB0, &t);
8438c2ecf20Sopenharmony_ci	v |= t;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	*signal = v;
8468c2ecf20Sopenharmony_ci	dprintk("%s: signal=0x%02X\n", __func__, *signal);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return 0;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
8568c2ecf20Sopenharmony_ci		return lgs8913_read_signal_strength(priv, signal);
8578c2ecf20Sopenharmony_ci	else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
8588c2ecf20Sopenharmony_ci		return lgs8g75_read_signal_strength(priv, signal);
8598c2ecf20Sopenharmony_ci	else
8608c2ecf20Sopenharmony_ci		return lgs8gxx_read_signal_agc(priv, signal);
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
8668c2ecf20Sopenharmony_ci	u8 t;
8678c2ecf20Sopenharmony_ci	*snr = 0;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
8708c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x34, &t);
8718c2ecf20Sopenharmony_ci	else
8728c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x95, &t);
8738c2ecf20Sopenharmony_ci	dprintk("AVG Noise=0x%02X\n", t);
8748c2ecf20Sopenharmony_ci	*snr = 256 - t;
8758c2ecf20Sopenharmony_ci	*snr <<= 8;
8768c2ecf20Sopenharmony_ci	dprintk("snr=0x%x\n", *snr);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	return 0;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	*ucblocks = 0;
8848c2ecf20Sopenharmony_ci	dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
8858c2ecf20Sopenharmony_ci	return 0;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic void packet_counter_start(struct lgs8gxx_state *priv)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	u8 orig, t;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
8938c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x30, &orig);
8948c2ecf20Sopenharmony_ci		orig &= 0xE7;
8958c2ecf20Sopenharmony_ci		t = orig | 0x10;
8968c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x30, t);
8978c2ecf20Sopenharmony_ci		t = orig | 0x18;
8988c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x30, t);
8998c2ecf20Sopenharmony_ci		t = orig | 0x10;
9008c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x30, t);
9018c2ecf20Sopenharmony_ci	} else {
9028c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC6, 0x01);
9038c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC6, 0x41);
9048c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC6, 0x01);
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void packet_counter_stop(struct lgs8gxx_state *priv)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	u8 t;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
9138c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, 0x30, &t);
9148c2ecf20Sopenharmony_ci		t &= 0xE7;
9158c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0x30, t);
9168c2ecf20Sopenharmony_ci	} else {
9178c2ecf20Sopenharmony_ci		lgs8gxx_write_reg(priv, 0xC6, 0x81);
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
9248c2ecf20Sopenharmony_ci	u8 reg_err, reg_total, t;
9258c2ecf20Sopenharmony_ci	u32 total_cnt = 0, err_cnt = 0;
9268c2ecf20Sopenharmony_ci	int i;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	packet_counter_start(priv);
9318c2ecf20Sopenharmony_ci	msleep(200);
9328c2ecf20Sopenharmony_ci	packet_counter_stop(priv);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
9358c2ecf20Sopenharmony_ci		reg_total = 0x28; reg_err = 0x2C;
9368c2ecf20Sopenharmony_ci	} else {
9378c2ecf20Sopenharmony_ci		reg_total = 0xD0; reg_err = 0xD4;
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
9418c2ecf20Sopenharmony_ci		total_cnt <<= 8;
9428c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, reg_total+3-i, &t);
9438c2ecf20Sopenharmony_ci		total_cnt |= t;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
9468c2ecf20Sopenharmony_ci		err_cnt <<= 8;
9478c2ecf20Sopenharmony_ci		lgs8gxx_read_reg(priv, reg_err+3-i, &t);
9488c2ecf20Sopenharmony_ci		err_cnt |= t;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci	dprintk("error=%d total=%d\n", err_cnt, total_cnt);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	if (total_cnt == 0)
9538c2ecf20Sopenharmony_ci		*ber = 0;
9548c2ecf20Sopenharmony_ci	else
9558c2ecf20Sopenharmony_ci		*ber = err_cnt * 100 / total_cnt;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	dprintk("%s: ber=0x%x\n", __func__, *ber);
9588c2ecf20Sopenharmony_ci	return 0;
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = fe->demodulator_priv;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (priv->config->tuner_address == 0)
9668c2ecf20Sopenharmony_ci		return 0;
9678c2ecf20Sopenharmony_ci	if (enable) {
9688c2ecf20Sopenharmony_ci		u8 v = 0x80 | priv->config->tuner_address;
9698c2ecf20Sopenharmony_ci		return lgs8gxx_write_reg(priv, 0x01, v);
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci	return lgs8gxx_write_reg(priv, 0x01, 0);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgs8gxx_ops = {
9758c2ecf20Sopenharmony_ci	.delsys = { SYS_DTMB },
9768c2ecf20Sopenharmony_ci	.info = {
9778c2ecf20Sopenharmony_ci		.name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
9788c2ecf20Sopenharmony_ci		.frequency_min_hz = 474 * MHz,
9798c2ecf20Sopenharmony_ci		.frequency_max_hz = 858 * MHz,
9808c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 10 * kHz,
9818c2ecf20Sopenharmony_ci		.caps =
9828c2ecf20Sopenharmony_ci			FE_CAN_FEC_AUTO |
9838c2ecf20Sopenharmony_ci			FE_CAN_QAM_AUTO |
9848c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
9858c2ecf20Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO
9868c2ecf20Sopenharmony_ci	},
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	.release = lgs8gxx_release,
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	.init = lgs8gxx_init,
9918c2ecf20Sopenharmony_ci	.write = lgs8gxx_write,
9928c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	.set_frontend = lgs8gxx_set_fe,
9958c2ecf20Sopenharmony_ci	.get_tune_settings = lgs8gxx_get_tune_settings,
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	.read_status = lgs8gxx_read_status,
9988c2ecf20Sopenharmony_ci	.read_ber = lgs8gxx_read_ber,
9998c2ecf20Sopenharmony_ci	.read_signal_strength = lgs8gxx_read_signal_strength,
10008c2ecf20Sopenharmony_ci	.read_snr = lgs8gxx_read_snr,
10018c2ecf20Sopenharmony_ci	.read_ucblocks = lgs8gxx_read_ucblocks,
10028c2ecf20Sopenharmony_ci};
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistruct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
10058c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct lgs8gxx_state *priv = NULL;
10088c2ecf20Sopenharmony_ci	u8 data = 0;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (config == NULL || i2c == NULL)
10138c2ecf20Sopenharmony_ci		return NULL;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
10168c2ecf20Sopenharmony_ci	if (priv == NULL)
10178c2ecf20Sopenharmony_ci		goto error_out;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	priv->config = config;
10208c2ecf20Sopenharmony_ci	priv->i2c = i2c;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* check if the demod is there */
10238c2ecf20Sopenharmony_ci	if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
10248c2ecf20Sopenharmony_ci		dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
10258c2ecf20Sopenharmony_ci			__func__, priv->config->demod_address);
10268c2ecf20Sopenharmony_ci		goto error_out;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	lgs8gxx_read_reg(priv, 1, &data);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	memcpy(&priv->frontend.ops, &lgs8gxx_ops,
10328c2ecf20Sopenharmony_ci	       sizeof(struct dvb_frontend_ops));
10338c2ecf20Sopenharmony_ci	priv->frontend.demodulator_priv = priv;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (config->prod == LGS8GXX_PROD_LGS8G75)
10368c2ecf20Sopenharmony_ci		lgs8g75_init_data(priv);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return &priv->frontend;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_cierror_out:
10418c2ecf20Sopenharmony_ci	dprintk("%s() error_out\n", __func__);
10428c2ecf20Sopenharmony_ci	kfree(priv);
10438c2ecf20Sopenharmony_ci	return NULL;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lgs8gxx_attach);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
10498c2ecf20Sopenharmony_ciMODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
10508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10518c2ecf20Sopenharmony_ciMODULE_FIRMWARE(LGS8GXX_FIRMWARE);
1052