18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2010-2014 Michael Krufky <mkrufky@linuxtv.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "mxl111sf-demod.h"
98c2ecf20Sopenharmony_ci#include "mxl111sf-reg.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/* debug */
128c2ecf20Sopenharmony_cistatic int mxl111sf_demod_debug;
138c2ecf20Sopenharmony_cimodule_param_named(debug, mxl111sf_demod_debug, int, 0644);
148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define mxl_dbg(fmt, arg...) \
178c2ecf20Sopenharmony_ci	if (mxl111sf_demod_debug) \
188c2ecf20Sopenharmony_ci		mxl_printk(KERN_DEBUG, fmt, ##arg)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct mxl111sf_demod_state {
238c2ecf20Sopenharmony_ci	struct mxl111sf_state *mxl_state;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	const struct mxl111sf_demod_config *cfg;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	struct dvb_frontend fe;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
338c2ecf20Sopenharmony_ci				   u8 addr, u8 *data)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return (state->cfg->read_reg) ?
368c2ecf20Sopenharmony_ci		state->cfg->read_reg(state->mxl_state, addr, data) :
378c2ecf20Sopenharmony_ci		-EINVAL;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
418c2ecf20Sopenharmony_ci				    u8 addr, u8 data)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return (state->cfg->write_reg) ?
448c2ecf20Sopenharmony_ci		state->cfg->write_reg(state->mxl_state, addr, data) :
458c2ecf20Sopenharmony_ci		-EINVAL;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic
498c2ecf20Sopenharmony_ciint mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
508c2ecf20Sopenharmony_ci				struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return (state->cfg->program_regs) ?
538c2ecf20Sopenharmony_ci		state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
548c2ecf20Sopenharmony_ci		-EINVAL;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
588c2ecf20Sopenharmony_ci/* TPS */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic
618c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
628c2ecf20Sopenharmony_ci				     enum fe_code_rate *code_rate)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	u8 val;
658c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
668c2ecf20Sopenharmony_ci	/* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
678c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
688c2ecf20Sopenharmony_ci		goto fail;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	switch (val & V6_CODE_RATE_TPS_MASK) {
718c2ecf20Sopenharmony_ci	case 0:
728c2ecf20Sopenharmony_ci		*code_rate = FEC_1_2;
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	case 1:
758c2ecf20Sopenharmony_ci		*code_rate = FEC_2_3;
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci	case 2:
788c2ecf20Sopenharmony_ci		*code_rate = FEC_3_4;
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci	case 3:
818c2ecf20Sopenharmony_ci		*code_rate = FEC_5_6;
828c2ecf20Sopenharmony_ci		break;
838c2ecf20Sopenharmony_ci	case 4:
848c2ecf20Sopenharmony_ci		*code_rate = FEC_7_8;
858c2ecf20Sopenharmony_ci		break;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_cifail:
888c2ecf20Sopenharmony_ci	return ret;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic
928c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state,
938c2ecf20Sopenharmony_ci				      enum fe_modulation *modulation)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	u8 val;
968c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
978c2ecf20Sopenharmony_ci	/* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
988c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
998c2ecf20Sopenharmony_ci		goto fail;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
1028c2ecf20Sopenharmony_ci	case 0:
1038c2ecf20Sopenharmony_ci		*modulation = QPSK;
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci	case 1:
1068c2ecf20Sopenharmony_ci		*modulation = QAM_16;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case 2:
1098c2ecf20Sopenharmony_ci		*modulation = QAM_64;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_cifail:
1138c2ecf20Sopenharmony_ci	return ret;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic
1178c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
1188c2ecf20Sopenharmony_ci					  enum fe_transmit_mode *fft_mode)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	u8 val;
1218c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
1228c2ecf20Sopenharmony_ci	/* FFT Mode, 00:2K, 01:8K, 10:4K */
1238c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
1248c2ecf20Sopenharmony_ci		goto fail;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
1278c2ecf20Sopenharmony_ci	case 0:
1288c2ecf20Sopenharmony_ci		*fft_mode = TRANSMISSION_MODE_2K;
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	case 1:
1318c2ecf20Sopenharmony_ci		*fft_mode = TRANSMISSION_MODE_8K;
1328c2ecf20Sopenharmony_ci		break;
1338c2ecf20Sopenharmony_ci	case 2:
1348c2ecf20Sopenharmony_ci		*fft_mode = TRANSMISSION_MODE_4K;
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_cifail:
1388c2ecf20Sopenharmony_ci	return ret;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic
1428c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
1438c2ecf20Sopenharmony_ci					  enum fe_guard_interval *guard)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	u8 val;
1468c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
1478c2ecf20Sopenharmony_ci	/* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
1488c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
1498c2ecf20Sopenharmony_ci		goto fail;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	switch ((val & V6_PARAM_GI_MASK) >> 4) {
1528c2ecf20Sopenharmony_ci	case 0:
1538c2ecf20Sopenharmony_ci		*guard = GUARD_INTERVAL_1_32;
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case 1:
1568c2ecf20Sopenharmony_ci		*guard = GUARD_INTERVAL_1_16;
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci	case 2:
1598c2ecf20Sopenharmony_ci		*guard = GUARD_INTERVAL_1_8;
1608c2ecf20Sopenharmony_ci		break;
1618c2ecf20Sopenharmony_ci	case 3:
1628c2ecf20Sopenharmony_ci		*guard = GUARD_INTERVAL_1_4;
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_cifail:
1668c2ecf20Sopenharmony_ci	return ret;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic
1708c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
1718c2ecf20Sopenharmony_ci				     enum fe_hierarchy *hierarchy)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	u8 val;
1748c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
1758c2ecf20Sopenharmony_ci	/* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
1768c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
1778c2ecf20Sopenharmony_ci		goto fail;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
1808c2ecf20Sopenharmony_ci	case 0:
1818c2ecf20Sopenharmony_ci		*hierarchy = HIERARCHY_NONE;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case 1:
1848c2ecf20Sopenharmony_ci		*hierarchy = HIERARCHY_1;
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci	case 2:
1878c2ecf20Sopenharmony_ci		*hierarchy = HIERARCHY_2;
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci	case 3:
1908c2ecf20Sopenharmony_ci		*hierarchy = HIERARCHY_4;
1918c2ecf20Sopenharmony_ci		break;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_cifail:
1948c2ecf20Sopenharmony_ci	return ret;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
1988c2ecf20Sopenharmony_ci/* LOCKS */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic
2018c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
2028c2ecf20Sopenharmony_ci					int *sync_lock)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u8 val = 0;
2058c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
2068c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
2078c2ecf20Sopenharmony_ci		goto fail;
2088c2ecf20Sopenharmony_ci	*sync_lock = (val & SYNC_LOCK_MASK) >> 4;
2098c2ecf20Sopenharmony_cifail:
2108c2ecf20Sopenharmony_ci	return ret;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic
2148c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
2158c2ecf20Sopenharmony_ci				      int *rs_lock)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	u8 val = 0;
2188c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
2198c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
2208c2ecf20Sopenharmony_ci		goto fail;
2218c2ecf20Sopenharmony_ci	*rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
2228c2ecf20Sopenharmony_cifail:
2238c2ecf20Sopenharmony_ci	return ret;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic
2278c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
2288c2ecf20Sopenharmony_ci				       int *tps_lock)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u8 val = 0;
2318c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
2328c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
2338c2ecf20Sopenharmony_ci		goto fail;
2348c2ecf20Sopenharmony_ci	*tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
2358c2ecf20Sopenharmony_cifail:
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic
2408c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
2418c2ecf20Sopenharmony_ci				       int *fec_lock)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	u8 val = 0;
2448c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
2458c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
2468c2ecf20Sopenharmony_ci		goto fail;
2478c2ecf20Sopenharmony_ci	*fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
2488c2ecf20Sopenharmony_cifail:
2498c2ecf20Sopenharmony_ci	return ret;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci#if 0
2538c2ecf20Sopenharmony_cistatic
2548c2ecf20Sopenharmony_ciint mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
2558c2ecf20Sopenharmony_ci				      int *cp_lock)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	u8 val = 0;
2588c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
2598c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
2608c2ecf20Sopenharmony_ci		goto fail;
2618c2ecf20Sopenharmony_ci	*cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
2628c2ecf20Sopenharmony_cifail:
2638c2ecf20Sopenharmony_ci	return ret;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci#endif
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int mxl111sf_demod_set_frontend(struct dvb_frontend *fe)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
2778c2ecf20Sopenharmony_ci	int ret = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
2808c2ecf20Sopenharmony_ci		{0x00, 0xff, 0x01}, /* change page to 1 */
2818c2ecf20Sopenharmony_ci		{0x40, 0xff, 0x05},
2828c2ecf20Sopenharmony_ci		{0x40, 0xff, 0x01},
2838c2ecf20Sopenharmony_ci		{0x41, 0xff, 0xca},
2848c2ecf20Sopenharmony_ci		{0x41, 0xff, 0xc0},
2858c2ecf20Sopenharmony_ci		{0x00, 0xff, 0x00}, /* change page to 0 */
2868c2ecf20Sopenharmony_ci		{0,    0,    0}
2878c2ecf20Sopenharmony_ci	};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	mxl_dbg("()");
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params) {
2928c2ecf20Sopenharmony_ci		ret = fe->ops.tuner_ops.set_params(fe);
2938c2ecf20Sopenharmony_ci		if (mxl_fail(ret))
2948c2ecf20Sopenharmony_ci			goto fail;
2958c2ecf20Sopenharmony_ci		msleep(50);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
2988c2ecf20Sopenharmony_ci	mxl_fail(ret);
2998c2ecf20Sopenharmony_ci	msleep(50);
3008c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_reset_irq_status(state);
3018c2ecf20Sopenharmony_ci	mxl_fail(ret);
3028c2ecf20Sopenharmony_ci	msleep(100);
3038c2ecf20Sopenharmony_cifail:
3048c2ecf20Sopenharmony_ci	return ret;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci#if 0
3108c2ecf20Sopenharmony_ci/* resets TS Packet error count */
3118c2ecf20Sopenharmony_ci/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
3128c2ecf20Sopenharmony_cistatic
3138c2ecf20Sopenharmony_ciint mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct mxl111sf_reg_ctrl_info reset_per_count[] = {
3168c2ecf20Sopenharmony_ci		{0x20, 0x01, 0x01},
3178c2ecf20Sopenharmony_ci		{0x20, 0x01, 0x00},
3188c2ecf20Sopenharmony_ci		{0,    0,    0}
3198c2ecf20Sopenharmony_ci	};
3208c2ecf20Sopenharmony_ci	return mxl111sf_demod_program_regs(state, reset_per_count);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci#endif
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/* returns TS Packet error count */
3258c2ecf20Sopenharmony_ci/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
3268c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
3298c2ecf20Sopenharmony_ci	u32 fec_per_count, fec_per_scale;
3308c2ecf20Sopenharmony_ci	u8 val;
3318c2ecf20Sopenharmony_ci	int ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	*ucblocks = 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* FEC_PER_COUNT Register */
3368c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
3378c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
3388c2ecf20Sopenharmony_ci		goto fail;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	fec_per_count = val;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* FEC_PER_SCALE Register */
3438c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
3448c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
3458c2ecf20Sopenharmony_ci		goto fail;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	val &= V6_FEC_PER_SCALE_MASK;
3488c2ecf20Sopenharmony_ci	val *= 4;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	fec_per_scale = 1 << val;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	fec_per_count *= fec_per_scale;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	*ucblocks = fec_per_count;
3558c2ecf20Sopenharmony_cifail:
3568c2ecf20Sopenharmony_ci	return ret;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
3608c2ecf20Sopenharmony_ci/* FIXME: leaving this enabled breaks the build on some architectures,
3618c2ecf20Sopenharmony_ci * and we shouldn't have any floating point math in the kernel, anyway.
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * These macros need to be re-written, but it's harmless to simply
3648c2ecf20Sopenharmony_ci * return zero for now. */
3658c2ecf20Sopenharmony_ci#define CALCULATE_BER(avg_errors, count) \
3668c2ecf20Sopenharmony_ci	((u32)(avg_errors * 4)/(count*64*188*8))
3678c2ecf20Sopenharmony_ci#define CALCULATE_SNR(data) \
3688c2ecf20Sopenharmony_ci	((u32)((10 * (u32)data / 64) - 2.5))
3698c2ecf20Sopenharmony_ci#else
3708c2ecf20Sopenharmony_ci#define CALCULATE_BER(avg_errors, count) 0
3718c2ecf20Sopenharmony_ci#define CALCULATE_SNR(data) 0
3728c2ecf20Sopenharmony_ci#endif
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
3778c2ecf20Sopenharmony_ci	u8 val1, val2, val3;
3788c2ecf20Sopenharmony_ci	int ret;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	*ber = 0;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
3838c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
3848c2ecf20Sopenharmony_ci		goto fail;
3858c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
3868c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
3878c2ecf20Sopenharmony_ci		goto fail;
3888c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
3898c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
3908c2ecf20Sopenharmony_ci		goto fail;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	*ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
3938c2ecf20Sopenharmony_cifail:
3948c2ecf20Sopenharmony_ci	return ret;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
3988c2ecf20Sopenharmony_ci				   u16 *snr)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	u8 val1, val2;
4018c2ecf20Sopenharmony_ci	int ret;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	*snr = 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
4068c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4078c2ecf20Sopenharmony_ci		goto fail;
4088c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
4098c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4108c2ecf20Sopenharmony_ci		goto fail;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	*snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
4138c2ecf20Sopenharmony_cifail:
4148c2ecf20Sopenharmony_ci	return ret;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	int ret = mxl111sf_demod_calc_snr(state, snr);
4228c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4238c2ecf20Sopenharmony_ci		goto fail;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	*snr /= 10; /* 0.1 dB */
4268c2ecf20Sopenharmony_cifail:
4278c2ecf20Sopenharmony_ci	return ret;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_status(struct dvb_frontend *fe,
4318c2ecf20Sopenharmony_ci				      enum fe_status *status)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
4348c2ecf20Sopenharmony_ci	int ret, locked, cr_lock, sync_lock, fec_lock;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	*status = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
4398c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4408c2ecf20Sopenharmony_ci		goto fail;
4418c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
4428c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4438c2ecf20Sopenharmony_ci		goto fail;
4448c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
4458c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4468c2ecf20Sopenharmony_ci		goto fail;
4478c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
4488c2ecf20Sopenharmony_ci	if (mxl_fail(ret))
4498c2ecf20Sopenharmony_ci		goto fail;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (locked)
4528c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL;
4538c2ecf20Sopenharmony_ci	if (cr_lock)
4548c2ecf20Sopenharmony_ci		*status |= FE_HAS_CARRIER;
4558c2ecf20Sopenharmony_ci	if (sync_lock)
4568c2ecf20Sopenharmony_ci		*status |= FE_HAS_SYNC;
4578c2ecf20Sopenharmony_ci	if (fec_lock) /* false positives? */
4588c2ecf20Sopenharmony_ci		*status |= FE_HAS_VITERBI;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if ((locked) && (cr_lock) && (sync_lock))
4618c2ecf20Sopenharmony_ci		*status |= FE_HAS_LOCK;
4628c2ecf20Sopenharmony_cifail:
4638c2ecf20Sopenharmony_ci	return ret;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
4678c2ecf20Sopenharmony_ci					       u16 *signal_strength)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
4708c2ecf20Sopenharmony_ci	enum fe_modulation modulation;
4718c2ecf20Sopenharmony_ci	int ret;
4728c2ecf20Sopenharmony_ci	u16 snr;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	ret = mxl111sf_demod_calc_snr(state, &snr);
4758c2ecf20Sopenharmony_ci	if (ret < 0)
4768c2ecf20Sopenharmony_ci		return ret;
4778c2ecf20Sopenharmony_ci	ret = mxl1x1sf_demod_get_tps_modulation(state, &modulation);
4788c2ecf20Sopenharmony_ci	if (ret < 0)
4798c2ecf20Sopenharmony_ci		return ret;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	switch (modulation) {
4828c2ecf20Sopenharmony_ci	case QPSK:
4838c2ecf20Sopenharmony_ci		*signal_strength = (snr >= 1300) ?
4848c2ecf20Sopenharmony_ci			min(65535, snr * 44) : snr * 38;
4858c2ecf20Sopenharmony_ci		break;
4868c2ecf20Sopenharmony_ci	case QAM_16:
4878c2ecf20Sopenharmony_ci		*signal_strength = (snr >= 1500) ?
4888c2ecf20Sopenharmony_ci			min(65535, snr * 38) : snr * 33;
4898c2ecf20Sopenharmony_ci		break;
4908c2ecf20Sopenharmony_ci	case QAM_64:
4918c2ecf20Sopenharmony_ci		*signal_strength = (snr >= 2000) ?
4928c2ecf20Sopenharmony_ci			min(65535, snr * 29) : snr * 25;
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci	default:
4958c2ecf20Sopenharmony_ci		*signal_strength = 0;
4968c2ecf20Sopenharmony_ci		return -EINVAL;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
5038c2ecf20Sopenharmony_ci				       struct dtv_frontend_properties *p)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	mxl_dbg("()");
5088c2ecf20Sopenharmony_ci#if 0
5098c2ecf20Sopenharmony_ci	p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
5108c2ecf20Sopenharmony_ci#endif
5118c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.get_bandwidth)
5128c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz);
5138c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.get_frequency)
5148c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
5158c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_HP);
5168c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_LP);
5178c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_modulation(state, &p->modulation);
5188c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_guard_fft_mode(state,
5198c2ecf20Sopenharmony_ci					      &p->transmission_mode);
5208c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_guard_interval(state,
5218c2ecf20Sopenharmony_ci					      &p->guard_interval);
5228c2ecf20Sopenharmony_ci	mxl1x1sf_demod_get_tps_hierarchy(state,
5238c2ecf20Sopenharmony_ci					 &p->hierarchy);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic
5298c2ecf20Sopenharmony_ciint mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
5308c2ecf20Sopenharmony_ci				     struct dvb_frontend_tune_settings *tune)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	tune->min_delay_ms = 1000;
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void mxl111sf_demod_release(struct dvb_frontend *fe)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = fe->demodulator_priv;
5398c2ecf20Sopenharmony_ci	mxl_dbg("()");
5408c2ecf20Sopenharmony_ci	kfree(state);
5418c2ecf20Sopenharmony_ci	fe->demodulator_priv = NULL;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops mxl111sf_demod_ops = {
5458c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBT },
5468c2ecf20Sopenharmony_ci	.info = {
5478c2ecf20Sopenharmony_ci		.name               = "MaxLinear MxL111SF DVB-T demodulator",
5488c2ecf20Sopenharmony_ci		.frequency_min_hz      = 177 * MHz,
5498c2ecf20Sopenharmony_ci		.frequency_max_hz      = 858 * MHz,
5508c2ecf20Sopenharmony_ci		.frequency_stepsize_hz = 166666,
5518c2ecf20Sopenharmony_ci		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
5528c2ecf20Sopenharmony_ci			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
5538c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
5548c2ecf20Sopenharmony_ci			FE_CAN_QAM_AUTO |
5558c2ecf20Sopenharmony_ci			FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
5568c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
5578c2ecf20Sopenharmony_ci	},
5588c2ecf20Sopenharmony_ci	.release              = mxl111sf_demod_release,
5598c2ecf20Sopenharmony_ci#if 0
5608c2ecf20Sopenharmony_ci	.init                 = mxl111sf_init,
5618c2ecf20Sopenharmony_ci	.i2c_gate_ctrl        = mxl111sf_i2c_gate_ctrl,
5628c2ecf20Sopenharmony_ci#endif
5638c2ecf20Sopenharmony_ci	.set_frontend         = mxl111sf_demod_set_frontend,
5648c2ecf20Sopenharmony_ci	.get_frontend         = mxl111sf_demod_get_frontend,
5658c2ecf20Sopenharmony_ci	.get_tune_settings    = mxl111sf_demod_get_tune_settings,
5668c2ecf20Sopenharmony_ci	.read_status          = mxl111sf_demod_read_status,
5678c2ecf20Sopenharmony_ci	.read_signal_strength = mxl111sf_demod_read_signal_strength,
5688c2ecf20Sopenharmony_ci	.read_ber             = mxl111sf_demod_read_ber,
5698c2ecf20Sopenharmony_ci	.read_snr             = mxl111sf_demod_read_snr,
5708c2ecf20Sopenharmony_ci	.read_ucblocks        = mxl111sf_demod_read_ucblocks,
5718c2ecf20Sopenharmony_ci};
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistruct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
5748c2ecf20Sopenharmony_ci				   const struct mxl111sf_demod_config *cfg)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct mxl111sf_demod_state *state = NULL;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	mxl_dbg("()");
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
5818c2ecf20Sopenharmony_ci	if (state == NULL)
5828c2ecf20Sopenharmony_ci		return NULL;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	state->mxl_state = mxl_state;
5858c2ecf20Sopenharmony_ci	state->cfg = cfg;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	memcpy(&state->fe.ops, &mxl111sf_demod_ops,
5888c2ecf20Sopenharmony_ci	       sizeof(struct dvb_frontend_ops));
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	state->fe.demodulator_priv = state;
5918c2ecf20Sopenharmony_ci	return &state->fe;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
5968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
5978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5988c2ecf20Sopenharmony_ciMODULE_VERSION("0.1");
599