18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
48c2ecf20Sopenharmony_ci *    ATBM8830, ATBM8831
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *    Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/div64.h>
108c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "atbm8830.h"
138c2ecf20Sopenharmony_ci#include "atbm8830_priv.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define dprintk(args...) \
168c2ecf20Sopenharmony_ci	do { \
178c2ecf20Sopenharmony_ci		if (debug) \
188c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "atbm8830: " args); \
198c2ecf20Sopenharmony_ci	} while (0)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int debug;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int ret = 0;
298c2ecf20Sopenharmony_ci	u8 dev_addr;
308c2ecf20Sopenharmony_ci	u8 buf1[] = { reg >> 8, reg & 0xFF };
318c2ecf20Sopenharmony_ci	u8 buf2[] = { data };
328c2ecf20Sopenharmony_ci	struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
338c2ecf20Sopenharmony_ci	struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 };
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	dev_addr = priv->config->demod_address;
368c2ecf20Sopenharmony_ci	msg1.addr = dev_addr;
378c2ecf20Sopenharmony_ci	msg2.addr = dev_addr;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (debug >= 2)
408c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg1, 1);
438c2ecf20Sopenharmony_ci	if (ret != 1)
448c2ecf20Sopenharmony_ci		return -EIO;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg2, 1);
478c2ecf20Sopenharmony_ci	return (ret != 1) ? -EIO : 0;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci	u8 dev_addr;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	u8 buf1[] = { reg >> 8, reg & 0xFF };
568c2ecf20Sopenharmony_ci	u8 buf2[] = { 0 };
578c2ecf20Sopenharmony_ci	struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
588c2ecf20Sopenharmony_ci	struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 };
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	dev_addr = priv->config->demod_address;
618c2ecf20Sopenharmony_ci	msg1.addr = dev_addr;
628c2ecf20Sopenharmony_ci	msg2.addr = dev_addr;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg1, 1);
658c2ecf20Sopenharmony_ci	if (ret != 1) {
668c2ecf20Sopenharmony_ci		dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret);
678c2ecf20Sopenharmony_ci		return -EIO;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = i2c_transfer(priv->i2c, &msg2, 1);
718c2ecf20Sopenharmony_ci	if (ret != 1)
728c2ecf20Sopenharmony_ci		return -EIO;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	*p_data = buf2[0];
758c2ecf20Sopenharmony_ci	if (debug >= 2)
768c2ecf20Sopenharmony_ci		dprintk("%s: reg=0x%04X, data=0x%02X\n",
778c2ecf20Sopenharmony_ci			__func__, reg, buf2[0]);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* Lock register latch so that multi-register read is atomic */
838c2ecf20Sopenharmony_cistatic inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	u32 val;
918c2ecf20Sopenharmony_ci	u64 t;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* 0x100000 * freq / 30.4MHz */
948c2ecf20Sopenharmony_ci	t = (u64)0x100000 * freq;
958c2ecf20Sopenharmony_ci	do_div(t, 30400);
968c2ecf20Sopenharmony_ci	val = t;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_OSC_CLK, val);
998c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8);
1008c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	u32 fs = priv->config->osc_clk_freq;
1098c2ecf20Sopenharmony_ci	u64 t;
1108c2ecf20Sopenharmony_ci	u32 val;
1118c2ecf20Sopenharmony_ci	u8 dat;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (freq != 0) {
1148c2ecf20Sopenharmony_ci		/* 2 * PI * (freq - fs) / fs * (2 ^ 22) */
1158c2ecf20Sopenharmony_ci		t = (u64) 2 * 31416 * (freq - fs);
1168c2ecf20Sopenharmony_ci		t <<= 22;
1178c2ecf20Sopenharmony_ci		do_div(t, fs);
1188c2ecf20Sopenharmony_ci		do_div(t, 1000);
1198c2ecf20Sopenharmony_ci		val = t;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1);
1228c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_IF_FREQ, val);
1238c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8);
1248c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
1278c2ecf20Sopenharmony_ci		dat &= 0xFC;
1288c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
1298c2ecf20Sopenharmony_ci	} else {
1308c2ecf20Sopenharmony_ci		/* Zero IF */
1318c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
1348c2ecf20Sopenharmony_ci		dat &= 0xFC;
1358c2ecf20Sopenharmony_ci		dat |= 0x02;
1368c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		if (priv->config->zif_swap_iq)
1398c2ecf20Sopenharmony_ci			atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03);
1408c2ecf20Sopenharmony_ci		else
1418c2ecf20Sopenharmony_ci			atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int is_locked(struct atbm_state *priv, u8 *locked)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	u8 status;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_LOCK_STATUS, &status);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (locked != NULL)
1548c2ecf20Sopenharmony_ci		*locked = (status == 1);
1558c2ecf20Sopenharmony_ci	return 0;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int set_agc_config(struct atbm_state *priv,
1598c2ecf20Sopenharmony_ci	u8 min, u8 max, u8 hold_loop)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	/* no effect if both min and max are zero */
1628c2ecf20Sopenharmony_ci	if (!min && !max)
1638c2ecf20Sopenharmony_ci	    return 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_AGC_MIN, min);
1668c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_AGC_MAX, max);
1678c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int set_static_channel_mode(struct atbm_state *priv)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	int i;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++)
1778c2ecf20Sopenharmony_ci		atbm8830_write_reg(priv, 0x099B + i, 0x08);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x095B, 0x7F);
1808c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x09CB, 0x01);
1818c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x09CC, 0x7F);
1828c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x09CD, 0x7F);
1838c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0E01, 0x20);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* For single carrier */
1868c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0B03, 0x0A);
1878c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0935, 0x10);
1888c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0936, 0x08);
1898c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x093E, 0x08);
1908c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x096E, 0x06);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* frame_count_max0 */
1938c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0B09, 0x00);
1948c2ecf20Sopenharmony_ci	/* frame_count_max1 */
1958c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x0B0A, 0x08);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int set_ts_config(struct atbm_state *priv)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	const struct atbm8830_config *cfg = priv->config;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*Set parallel/serial ts mode*/
2058c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0);
2068c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0);
2078c2ecf20Sopenharmony_ci	/*Set ts sampling edge*/
2088c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE,
2098c2ecf20Sopenharmony_ci		cfg->ts_sampling_edge ? 1 : 0);
2108c2ecf20Sopenharmony_ci	/*Set ts clock freerun*/
2118c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_TS_CLK_FREERUN,
2128c2ecf20Sopenharmony_ci		cfg->ts_clk_gated ? 0 : 1);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int atbm8830_init(struct dvb_frontend *fe)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
2208c2ecf20Sopenharmony_ci	const struct atbm8830_config *cfg = priv->config;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/*Set oscillator frequency*/
2238c2ecf20Sopenharmony_ci	set_osc_freq(priv, cfg->osc_clk_freq);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/*Set IF frequency*/
2268c2ecf20Sopenharmony_ci	set_if_freq(priv, cfg->if_freq);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/*Set AGC Config*/
2298c2ecf20Sopenharmony_ci	set_agc_config(priv, cfg->agc_min, cfg->agc_max,
2308c2ecf20Sopenharmony_ci		cfg->agc_hold_loop);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/*Set static channel mode*/
2338c2ecf20Sopenharmony_ci	set_static_channel_mode(priv);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	set_ts_config(priv);
2368c2ecf20Sopenharmony_ci	/*Turn off DSP reset*/
2378c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x000A, 0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/*SW version test*/
2408c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, 0x020C, 11);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Run */
2438c2ecf20Sopenharmony_ci	atbm8830_write_reg(priv, REG_DEMOD_RUN, 1);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return 0;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void atbm8830_release(struct dvb_frontend *fe)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct atbm_state *state = fe->demodulator_priv;
2528c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	kfree(state);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int atbm8830_set_fe(struct dvb_frontend *fe)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
2608c2ecf20Sopenharmony_ci	int i;
2618c2ecf20Sopenharmony_ci	u8 locked = 0;
2628c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* set frequency */
2658c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
2668c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl)
2678c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 1);
2688c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
2698c2ecf20Sopenharmony_ci		if (fe->ops.i2c_gate_ctrl)
2708c2ecf20Sopenharmony_ci			fe->ops.i2c_gate_ctrl(fe, 0);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* start auto lock */
2748c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
2758c2ecf20Sopenharmony_ci		mdelay(100);
2768c2ecf20Sopenharmony_ci		dprintk("Try %d\n", i);
2778c2ecf20Sopenharmony_ci		is_locked(priv, &locked);
2788c2ecf20Sopenharmony_ci		if (locked != 0) {
2798c2ecf20Sopenharmony_ci			dprintk("ATBM8830 locked!\n");
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int atbm8830_get_fe(struct dvb_frontend *fe,
2888c2ecf20Sopenharmony_ci			   struct dtv_frontend_properties *c)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* TODO: get real readings from device */
2938c2ecf20Sopenharmony_ci	/* inversion status */
2948c2ecf20Sopenharmony_ci	c->inversion = INVERSION_OFF;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* bandwidth */
2978c2ecf20Sopenharmony_ci	c->bandwidth_hz = 8000000;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	c->code_rate_HP = FEC_AUTO;
3008c2ecf20Sopenharmony_ci	c->code_rate_LP = FEC_AUTO;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	c->modulation = QAM_AUTO;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* transmission mode */
3058c2ecf20Sopenharmony_ci	c->transmission_mode = TRANSMISSION_MODE_AUTO;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* guard interval */
3088c2ecf20Sopenharmony_ci	c->guard_interval = GUARD_INTERVAL_AUTO;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* hierarchy */
3118c2ecf20Sopenharmony_ci	c->hierarchy = HIERARCHY_NONE;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int atbm8830_get_tune_settings(struct dvb_frontend *fe,
3178c2ecf20Sopenharmony_ci	struct dvb_frontend_tune_settings *fesettings)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	fesettings->min_delay_ms = 0;
3208c2ecf20Sopenharmony_ci	fesettings->step_size = 0;
3218c2ecf20Sopenharmony_ci	fesettings->max_drift = 0;
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int atbm8830_read_status(struct dvb_frontend *fe,
3268c2ecf20Sopenharmony_ci				enum fe_status *fe_status)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
3298c2ecf20Sopenharmony_ci	u8 locked = 0;
3308c2ecf20Sopenharmony_ci	u8 agc_locked = 0;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3338c2ecf20Sopenharmony_ci	*fe_status = 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	is_locked(priv, &locked);
3368c2ecf20Sopenharmony_ci	if (locked) {
3378c2ecf20Sopenharmony_ci		*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
3388c2ecf20Sopenharmony_ci			FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked);
3438c2ecf20Sopenharmony_ci	dprintk("AGC Lock: %d\n", agc_locked);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
3518c2ecf20Sopenharmony_ci	u32 frame_err;
3528c2ecf20Sopenharmony_ci	u8 t;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	atbm8830_reglatch_lock(priv, 1);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t);
3598c2ecf20Sopenharmony_ci	frame_err = t & 0x7F;
3608c2ecf20Sopenharmony_ci	frame_err <<= 8;
3618c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t);
3628c2ecf20Sopenharmony_ci	frame_err |= t;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	atbm8830_reglatch_lock(priv, 0);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	*ber = frame_err * 100 / 32767;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	dprintk("%s: ber=0x%x\n", __func__, *ber);
3698c2ecf20Sopenharmony_ci	return 0;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
3758c2ecf20Sopenharmony_ci	u32 pwm;
3768c2ecf20Sopenharmony_ci	u8 t;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
3798c2ecf20Sopenharmony_ci	atbm8830_reglatch_lock(priv, 1);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t);
3828c2ecf20Sopenharmony_ci	pwm = t & 0x03;
3838c2ecf20Sopenharmony_ci	pwm <<= 8;
3848c2ecf20Sopenharmony_ci	atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t);
3858c2ecf20Sopenharmony_ci	pwm |= t;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	atbm8830_reglatch_lock(priv, 0);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	dprintk("AGC PWM = 0x%02X\n", pwm);
3908c2ecf20Sopenharmony_ci	pwm = 0x400 - pwm;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	*signal = pwm * 0x10000 / 0x400;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
4008c2ecf20Sopenharmony_ci	*snr = 0;
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
4078c2ecf20Sopenharmony_ci	*ucblocks = 0;
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct atbm_state *priv = fe->demodulator_priv;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops atbm8830_ops = {
4198c2ecf20Sopenharmony_ci	.delsys = { SYS_DTMB },
4208c2ecf20Sopenharmony_ci	.info = {
4218c2ecf20Sopenharmony_ci		.name = "AltoBeam ATBM8830/8831 DMB-TH",
4228c2ecf20Sopenharmony_ci		.frequency_min_hz = 474 * MHz,
4238c2ecf20Sopenharmony_ci		.frequency_max_hz = 858 * MHz,
4248c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 10 * kHz,
4258c2ecf20Sopenharmony_ci		.caps =
4268c2ecf20Sopenharmony_ci			FE_CAN_FEC_AUTO |
4278c2ecf20Sopenharmony_ci			FE_CAN_QAM_AUTO |
4288c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
4298c2ecf20Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO
4308c2ecf20Sopenharmony_ci	},
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	.release = atbm8830_release,
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	.init = atbm8830_init,
4358c2ecf20Sopenharmony_ci	.sleep = NULL,
4368c2ecf20Sopenharmony_ci	.write = NULL,
4378c2ecf20Sopenharmony_ci	.i2c_gate_ctrl = atbm8830_i2c_gate_ctrl,
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	.set_frontend = atbm8830_set_fe,
4408c2ecf20Sopenharmony_ci	.get_frontend = atbm8830_get_fe,
4418c2ecf20Sopenharmony_ci	.get_tune_settings = atbm8830_get_tune_settings,
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	.read_status = atbm8830_read_status,
4448c2ecf20Sopenharmony_ci	.read_ber = atbm8830_read_ber,
4458c2ecf20Sopenharmony_ci	.read_signal_strength = atbm8830_read_signal_strength,
4468c2ecf20Sopenharmony_ci	.read_snr = atbm8830_read_snr,
4478c2ecf20Sopenharmony_ci	.read_ucblocks = atbm8830_read_ucblocks,
4488c2ecf20Sopenharmony_ci};
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistruct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
4518c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct atbm_state *priv = NULL;
4548c2ecf20Sopenharmony_ci	u8 data = 0;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (config == NULL || i2c == NULL)
4598c2ecf20Sopenharmony_ci		return NULL;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL);
4628c2ecf20Sopenharmony_ci	if (priv == NULL)
4638c2ecf20Sopenharmony_ci		goto error_out;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	priv->config = config;
4668c2ecf20Sopenharmony_ci	priv->i2c = i2c;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* check if the demod is there */
4698c2ecf20Sopenharmony_ci	if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) {
4708c2ecf20Sopenharmony_ci		dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n",
4718c2ecf20Sopenharmony_ci			__func__, priv->config->demod_address);
4728c2ecf20Sopenharmony_ci		goto error_out;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	dprintk("atbm8830 chip id: 0x%02X\n", data);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	memcpy(&priv->frontend.ops, &atbm8830_ops,
4778c2ecf20Sopenharmony_ci	       sizeof(struct dvb_frontend_ops));
4788c2ecf20Sopenharmony_ci	priv->frontend.demodulator_priv = priv;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	atbm8830_init(&priv->frontend);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	atbm8830_i2c_gate_ctrl(&priv->frontend, 1);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return &priv->frontend;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cierror_out:
4878c2ecf20Sopenharmony_ci	dprintk("%s() error_out\n", __func__);
4888c2ecf20Sopenharmony_ci	kfree(priv);
4898c2ecf20Sopenharmony_ci	return NULL;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atbm8830_attach);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
4958c2ecf20Sopenharmony_ciMODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
4968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
497