18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * E3C EC100 demodulator driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
98c2ecf20Sopenharmony_ci#include "ec100.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistruct ec100_state {
128c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c;
138c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
148c2ecf20Sopenharmony_ci	struct ec100_config config;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	u16 ber;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* write single register */
208c2ecf20Sopenharmony_cistatic int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	int ret;
238c2ecf20Sopenharmony_ci	u8 buf[2] = {reg, val};
248c2ecf20Sopenharmony_ci	struct i2c_msg msg[1] = {
258c2ecf20Sopenharmony_ci		{
268c2ecf20Sopenharmony_ci			.addr = state->config.demod_address,
278c2ecf20Sopenharmony_ci			.flags = 0,
288c2ecf20Sopenharmony_ci			.len = sizeof(buf),
298c2ecf20Sopenharmony_ci			.buf = buf,
308c2ecf20Sopenharmony_ci		}
318c2ecf20Sopenharmony_ci	};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 1);
348c2ecf20Sopenharmony_ci	if (ret == 1) {
358c2ecf20Sopenharmony_ci		ret = 0;
368c2ecf20Sopenharmony_ci	} else {
378c2ecf20Sopenharmony_ci		dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
388c2ecf20Sopenharmony_ci				KBUILD_MODNAME, ret, reg);
398c2ecf20Sopenharmony_ci		ret = -EREMOTEIO;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return ret;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* read single register */
468c2ecf20Sopenharmony_cistatic int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	int ret;
498c2ecf20Sopenharmony_ci	struct i2c_msg msg[2] = {
508c2ecf20Sopenharmony_ci		{
518c2ecf20Sopenharmony_ci			.addr = state->config.demod_address,
528c2ecf20Sopenharmony_ci			.flags = 0,
538c2ecf20Sopenharmony_ci			.len = 1,
548c2ecf20Sopenharmony_ci			.buf = &reg
558c2ecf20Sopenharmony_ci		}, {
568c2ecf20Sopenharmony_ci			.addr = state->config.demod_address,
578c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
588c2ecf20Sopenharmony_ci			.len = 1,
598c2ecf20Sopenharmony_ci			.buf = val
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci	};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 2);
648c2ecf20Sopenharmony_ci	if (ret == 2) {
658c2ecf20Sopenharmony_ci		ret = 0;
668c2ecf20Sopenharmony_ci	} else {
678c2ecf20Sopenharmony_ci		dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n",
688c2ecf20Sopenharmony_ci				KBUILD_MODNAME, ret, reg);
698c2ecf20Sopenharmony_ci		ret = -EREMOTEIO;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return ret;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int ec100_set_frontend(struct dvb_frontend *fe)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
788c2ecf20Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
798c2ecf20Sopenharmony_ci	int ret;
808c2ecf20Sopenharmony_ci	u8 tmp, tmp2;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
838c2ecf20Sopenharmony_ci			__func__, c->frequency, c->bandwidth_hz);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* program tuner */
868c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params)
878c2ecf20Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x04, 0x06);
908c2ecf20Sopenharmony_ci	if (ret)
918c2ecf20Sopenharmony_ci		goto error;
928c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x67, 0x58);
938c2ecf20Sopenharmony_ci	if (ret)
948c2ecf20Sopenharmony_ci		goto error;
958c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x05, 0x18);
968c2ecf20Sopenharmony_ci	if (ret)
978c2ecf20Sopenharmony_ci		goto error;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* reg/bw |   6  |   7  |   8
1008c2ecf20Sopenharmony_ci	   -------+------+------+------
1018c2ecf20Sopenharmony_ci	   A 0x1b | 0xa1 | 0xe7 | 0x2c
1028c2ecf20Sopenharmony_ci	   A 0x1c | 0x55 | 0x63 | 0x72
1038c2ecf20Sopenharmony_ci	   -------+------+------+------
1048c2ecf20Sopenharmony_ci	   B 0x1b | 0xb7 | 0x00 | 0x49
1058c2ecf20Sopenharmony_ci	   B 0x1c | 0x55 | 0x64 | 0x72 */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	switch (c->bandwidth_hz) {
1088c2ecf20Sopenharmony_ci	case 6000000:
1098c2ecf20Sopenharmony_ci		tmp = 0xb7;
1108c2ecf20Sopenharmony_ci		tmp2 = 0x55;
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	case 7000000:
1138c2ecf20Sopenharmony_ci		tmp = 0x00;
1148c2ecf20Sopenharmony_ci		tmp2 = 0x64;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case 8000000:
1178c2ecf20Sopenharmony_ci	default:
1188c2ecf20Sopenharmony_ci		tmp = 0x49;
1198c2ecf20Sopenharmony_ci		tmp2 = 0x72;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x1b, tmp);
1238c2ecf20Sopenharmony_ci	if (ret)
1248c2ecf20Sopenharmony_ci		goto error;
1258c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x1c, tmp2);
1268c2ecf20Sopenharmony_ci	if (ret)
1278c2ecf20Sopenharmony_ci		goto error;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
1308c2ecf20Sopenharmony_ci	if (ret)
1318c2ecf20Sopenharmony_ci		goto error;
1328c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
1338c2ecf20Sopenharmony_ci	if (ret)
1348c2ecf20Sopenharmony_ci		goto error;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x08, 0x24);
1378c2ecf20Sopenharmony_ci	if (ret)
1388c2ecf20Sopenharmony_ci		goto error;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x00, 0x00); /* go */
1418c2ecf20Sopenharmony_ci	if (ret)
1428c2ecf20Sopenharmony_ci		goto error;
1438c2ecf20Sopenharmony_ci	ret = ec100_write_reg(state, 0x00, 0x20); /* go */
1448c2ecf20Sopenharmony_ci	if (ret)
1458c2ecf20Sopenharmony_ci		goto error;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_cierror:
1498c2ecf20Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
1508c2ecf20Sopenharmony_ci	return ret;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int ec100_get_tune_settings(struct dvb_frontend *fe,
1548c2ecf20Sopenharmony_ci	struct dvb_frontend_tune_settings *fesettings)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	fesettings->min_delay_ms = 300;
1578c2ecf20Sopenharmony_ci	fesettings->step_size = 0;
1588c2ecf20Sopenharmony_ci	fesettings->max_drift = 0;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int ec100_read_status(struct dvb_frontend *fe, enum fe_status *status)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
1668c2ecf20Sopenharmony_ci	int ret;
1678c2ecf20Sopenharmony_ci	u8 tmp;
1688c2ecf20Sopenharmony_ci	*status = 0;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = ec100_read_reg(state, 0x42, &tmp);
1718c2ecf20Sopenharmony_ci	if (ret)
1728c2ecf20Sopenharmony_ci		goto error;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (tmp & 0x80) {
1758c2ecf20Sopenharmony_ci		/* bit7 set - have lock */
1768c2ecf20Sopenharmony_ci		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
1778c2ecf20Sopenharmony_ci			FE_HAS_SYNC | FE_HAS_LOCK;
1788c2ecf20Sopenharmony_ci	} else {
1798c2ecf20Sopenharmony_ci		ret = ec100_read_reg(state, 0x01, &tmp);
1808c2ecf20Sopenharmony_ci		if (ret)
1818c2ecf20Sopenharmony_ci			goto error;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		if (tmp & 0x10) {
1848c2ecf20Sopenharmony_ci			/* bit4 set - have signal */
1858c2ecf20Sopenharmony_ci			*status |= FE_HAS_SIGNAL;
1868c2ecf20Sopenharmony_ci			if (!(tmp & 0x01)) {
1878c2ecf20Sopenharmony_ci				/* bit0 clear - have ~valid signal */
1888c2ecf20Sopenharmony_ci				*status |= FE_HAS_CARRIER |  FE_HAS_VITERBI;
1898c2ecf20Sopenharmony_ci			}
1908c2ecf20Sopenharmony_ci		}
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return ret;
1948c2ecf20Sopenharmony_cierror:
1958c2ecf20Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
1968c2ecf20Sopenharmony_ci	return ret;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
2028c2ecf20Sopenharmony_ci	int ret;
2038c2ecf20Sopenharmony_ci	u8 tmp, tmp2;
2048c2ecf20Sopenharmony_ci	u16 ber2;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	*ber = 0;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ret = ec100_read_reg(state, 0x65, &tmp);
2098c2ecf20Sopenharmony_ci	if (ret)
2108c2ecf20Sopenharmony_ci		goto error;
2118c2ecf20Sopenharmony_ci	ret = ec100_read_reg(state, 0x66, &tmp2);
2128c2ecf20Sopenharmony_ci	if (ret)
2138c2ecf20Sopenharmony_ci		goto error;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	ber2 = (tmp2 << 8) | tmp;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* if counter overflow or clear */
2188c2ecf20Sopenharmony_ci	if (ber2 < state->ber)
2198c2ecf20Sopenharmony_ci		*ber = ber2;
2208c2ecf20Sopenharmony_ci	else
2218c2ecf20Sopenharmony_ci		*ber = ber2 - state->ber;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	state->ber = ber2;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return ret;
2268c2ecf20Sopenharmony_cierror:
2278c2ecf20Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
2288c2ecf20Sopenharmony_ci	return ret;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
2348c2ecf20Sopenharmony_ci	int ret;
2358c2ecf20Sopenharmony_ci	u8 tmp;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ret = ec100_read_reg(state, 0x24, &tmp);
2388c2ecf20Sopenharmony_ci	if (ret) {
2398c2ecf20Sopenharmony_ci		*strength = 0;
2408c2ecf20Sopenharmony_ci		goto error;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	*strength = ((tmp << 8) | tmp);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return ret;
2468c2ecf20Sopenharmony_cierror:
2478c2ecf20Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
2488c2ecf20Sopenharmony_ci	return ret;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	*snr = 0;
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	*ucblocks = 0;
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void ec100_release(struct dvb_frontend *fe)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
2668c2ecf20Sopenharmony_ci	kfree(state);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops ec100_ops;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistruct dvb_frontend *ec100_attach(const struct ec100_config *config,
2728c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	int ret;
2758c2ecf20Sopenharmony_ci	struct ec100_state *state = NULL;
2768c2ecf20Sopenharmony_ci	u8 tmp;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* allocate memory for the internal state */
2798c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
2808c2ecf20Sopenharmony_ci	if (state == NULL)
2818c2ecf20Sopenharmony_ci		goto error;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* setup the state */
2848c2ecf20Sopenharmony_ci	state->i2c = i2c;
2858c2ecf20Sopenharmony_ci	memcpy(&state->config, config, sizeof(struct ec100_config));
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* check if the demod is there */
2888c2ecf20Sopenharmony_ci	ret = ec100_read_reg(state, 0x33, &tmp);
2898c2ecf20Sopenharmony_ci	if (ret || tmp != 0x0b)
2908c2ecf20Sopenharmony_ci		goto error;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* create dvb_frontend */
2938c2ecf20Sopenharmony_ci	memcpy(&state->frontend.ops, &ec100_ops,
2948c2ecf20Sopenharmony_ci		sizeof(struct dvb_frontend_ops));
2958c2ecf20Sopenharmony_ci	state->frontend.demodulator_priv = state;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return &state->frontend;
2988c2ecf20Sopenharmony_cierror:
2998c2ecf20Sopenharmony_ci	kfree(state);
3008c2ecf20Sopenharmony_ci	return NULL;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ec100_attach);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops ec100_ops = {
3058c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBT },
3068c2ecf20Sopenharmony_ci	.info = {
3078c2ecf20Sopenharmony_ci		.name = "E3C EC100 DVB-T",
3088c2ecf20Sopenharmony_ci		.caps =
3098c2ecf20Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
3108c2ecf20Sopenharmony_ci			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
3118c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_QAM_16 |
3128c2ecf20Sopenharmony_ci			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
3138c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
3148c2ecf20Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO |
3158c2ecf20Sopenharmony_ci			FE_CAN_HIERARCHY_AUTO |
3168c2ecf20Sopenharmony_ci			FE_CAN_MUTE_TS
3178c2ecf20Sopenharmony_ci	},
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	.release = ec100_release,
3208c2ecf20Sopenharmony_ci	.set_frontend = ec100_set_frontend,
3218c2ecf20Sopenharmony_ci	.get_tune_settings = ec100_get_tune_settings,
3228c2ecf20Sopenharmony_ci	.read_status = ec100_read_status,
3238c2ecf20Sopenharmony_ci	.read_ber = ec100_read_ber,
3248c2ecf20Sopenharmony_ci	.read_signal_strength = ec100_read_signal_strength,
3258c2ecf20Sopenharmony_ci	.read_snr = ec100_read_snr,
3268c2ecf20Sopenharmony_ci	.read_ucblocks = ec100_read_ucblocks,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
3308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
3318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
332