162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * E3C EC100 demodulator driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <media/dvb_frontend.h>
962306a36Sopenharmony_ci#include "ec100.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct ec100_state {
1262306a36Sopenharmony_ci	struct i2c_adapter *i2c;
1362306a36Sopenharmony_ci	struct dvb_frontend frontend;
1462306a36Sopenharmony_ci	struct ec100_config config;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	u16 ber;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* write single register */
2062306a36Sopenharmony_cistatic int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	int ret;
2362306a36Sopenharmony_ci	u8 buf[2] = {reg, val};
2462306a36Sopenharmony_ci	struct i2c_msg msg[1] = {
2562306a36Sopenharmony_ci		{
2662306a36Sopenharmony_ci			.addr = state->config.demod_address,
2762306a36Sopenharmony_ci			.flags = 0,
2862306a36Sopenharmony_ci			.len = sizeof(buf),
2962306a36Sopenharmony_ci			.buf = buf,
3062306a36Sopenharmony_ci		}
3162306a36Sopenharmony_ci	};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 1);
3462306a36Sopenharmony_ci	if (ret == 1) {
3562306a36Sopenharmony_ci		ret = 0;
3662306a36Sopenharmony_ci	} else {
3762306a36Sopenharmony_ci		dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
3862306a36Sopenharmony_ci				KBUILD_MODNAME, ret, reg);
3962306a36Sopenharmony_ci		ret = -EREMOTEIO;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return ret;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* read single register */
4662306a36Sopenharmony_cistatic int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int ret;
4962306a36Sopenharmony_ci	struct i2c_msg msg[2] = {
5062306a36Sopenharmony_ci		{
5162306a36Sopenharmony_ci			.addr = state->config.demod_address,
5262306a36Sopenharmony_ci			.flags = 0,
5362306a36Sopenharmony_ci			.len = 1,
5462306a36Sopenharmony_ci			.buf = &reg
5562306a36Sopenharmony_ci		}, {
5662306a36Sopenharmony_ci			.addr = state->config.demod_address,
5762306a36Sopenharmony_ci			.flags = I2C_M_RD,
5862306a36Sopenharmony_ci			.len = 1,
5962306a36Sopenharmony_ci			.buf = val
6062306a36Sopenharmony_ci		}
6162306a36Sopenharmony_ci	};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = i2c_transfer(state->i2c, msg, 2);
6462306a36Sopenharmony_ci	if (ret == 2) {
6562306a36Sopenharmony_ci		ret = 0;
6662306a36Sopenharmony_ci	} else {
6762306a36Sopenharmony_ci		dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n",
6862306a36Sopenharmony_ci				KBUILD_MODNAME, ret, reg);
6962306a36Sopenharmony_ci		ret = -EREMOTEIO;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return ret;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int ec100_set_frontend(struct dvb_frontend *fe)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
7862306a36Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci	u8 tmp, tmp2;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
8362306a36Sopenharmony_ci			__func__, c->frequency, c->bandwidth_hz);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* program tuner */
8662306a36Sopenharmony_ci	if (fe->ops.tuner_ops.set_params)
8762306a36Sopenharmony_ci		fe->ops.tuner_ops.set_params(fe);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x04, 0x06);
9062306a36Sopenharmony_ci	if (ret)
9162306a36Sopenharmony_ci		goto error;
9262306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x67, 0x58);
9362306a36Sopenharmony_ci	if (ret)
9462306a36Sopenharmony_ci		goto error;
9562306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x05, 0x18);
9662306a36Sopenharmony_ci	if (ret)
9762306a36Sopenharmony_ci		goto error;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* reg/bw |   6  |   7  |   8
10062306a36Sopenharmony_ci	   -------+------+------+------
10162306a36Sopenharmony_ci	   A 0x1b | 0xa1 | 0xe7 | 0x2c
10262306a36Sopenharmony_ci	   A 0x1c | 0x55 | 0x63 | 0x72
10362306a36Sopenharmony_ci	   -------+------+------+------
10462306a36Sopenharmony_ci	   B 0x1b | 0xb7 | 0x00 | 0x49
10562306a36Sopenharmony_ci	   B 0x1c | 0x55 | 0x64 | 0x72 */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	switch (c->bandwidth_hz) {
10862306a36Sopenharmony_ci	case 6000000:
10962306a36Sopenharmony_ci		tmp = 0xb7;
11062306a36Sopenharmony_ci		tmp2 = 0x55;
11162306a36Sopenharmony_ci		break;
11262306a36Sopenharmony_ci	case 7000000:
11362306a36Sopenharmony_ci		tmp = 0x00;
11462306a36Sopenharmony_ci		tmp2 = 0x64;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case 8000000:
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		tmp = 0x49;
11962306a36Sopenharmony_ci		tmp2 = 0x72;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x1b, tmp);
12362306a36Sopenharmony_ci	if (ret)
12462306a36Sopenharmony_ci		goto error;
12562306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x1c, tmp2);
12662306a36Sopenharmony_ci	if (ret)
12762306a36Sopenharmony_ci		goto error;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
13062306a36Sopenharmony_ci	if (ret)
13162306a36Sopenharmony_ci		goto error;
13262306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
13362306a36Sopenharmony_ci	if (ret)
13462306a36Sopenharmony_ci		goto error;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x08, 0x24);
13762306a36Sopenharmony_ci	if (ret)
13862306a36Sopenharmony_ci		goto error;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x00, 0x00); /* go */
14162306a36Sopenharmony_ci	if (ret)
14262306a36Sopenharmony_ci		goto error;
14362306a36Sopenharmony_ci	ret = ec100_write_reg(state, 0x00, 0x20); /* go */
14462306a36Sopenharmony_ci	if (ret)
14562306a36Sopenharmony_ci		goto error;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return ret;
14862306a36Sopenharmony_cierror:
14962306a36Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
15062306a36Sopenharmony_ci	return ret;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int ec100_get_tune_settings(struct dvb_frontend *fe,
15462306a36Sopenharmony_ci	struct dvb_frontend_tune_settings *fesettings)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	fesettings->min_delay_ms = 300;
15762306a36Sopenharmony_ci	fesettings->step_size = 0;
15862306a36Sopenharmony_ci	fesettings->max_drift = 0;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int ec100_read_status(struct dvb_frontend *fe, enum fe_status *status)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
16662306a36Sopenharmony_ci	int ret;
16762306a36Sopenharmony_ci	u8 tmp;
16862306a36Sopenharmony_ci	*status = 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ret = ec100_read_reg(state, 0x42, &tmp);
17162306a36Sopenharmony_ci	if (ret)
17262306a36Sopenharmony_ci		goto error;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (tmp & 0x80) {
17562306a36Sopenharmony_ci		/* bit7 set - have lock */
17662306a36Sopenharmony_ci		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
17762306a36Sopenharmony_ci			FE_HAS_SYNC | FE_HAS_LOCK;
17862306a36Sopenharmony_ci	} else {
17962306a36Sopenharmony_ci		ret = ec100_read_reg(state, 0x01, &tmp);
18062306a36Sopenharmony_ci		if (ret)
18162306a36Sopenharmony_ci			goto error;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (tmp & 0x10) {
18462306a36Sopenharmony_ci			/* bit4 set - have signal */
18562306a36Sopenharmony_ci			*status |= FE_HAS_SIGNAL;
18662306a36Sopenharmony_ci			if (!(tmp & 0x01)) {
18762306a36Sopenharmony_ci				/* bit0 clear - have ~valid signal */
18862306a36Sopenharmony_ci				*status |= FE_HAS_CARRIER |  FE_HAS_VITERBI;
18962306a36Sopenharmony_ci			}
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return ret;
19462306a36Sopenharmony_cierror:
19562306a36Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
19662306a36Sopenharmony_ci	return ret;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
20262306a36Sopenharmony_ci	int ret;
20362306a36Sopenharmony_ci	u8 tmp, tmp2;
20462306a36Sopenharmony_ci	u16 ber2;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	*ber = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ret = ec100_read_reg(state, 0x65, &tmp);
20962306a36Sopenharmony_ci	if (ret)
21062306a36Sopenharmony_ci		goto error;
21162306a36Sopenharmony_ci	ret = ec100_read_reg(state, 0x66, &tmp2);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		goto error;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ber2 = (tmp2 << 8) | tmp;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* if counter overflow or clear */
21862306a36Sopenharmony_ci	if (ber2 < state->ber)
21962306a36Sopenharmony_ci		*ber = ber2;
22062306a36Sopenharmony_ci	else
22162306a36Sopenharmony_ci		*ber = ber2 - state->ber;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	state->ber = ber2;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return ret;
22662306a36Sopenharmony_cierror:
22762306a36Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
22862306a36Sopenharmony_ci	return ret;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
23462306a36Sopenharmony_ci	int ret;
23562306a36Sopenharmony_ci	u8 tmp;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = ec100_read_reg(state, 0x24, &tmp);
23862306a36Sopenharmony_ci	if (ret) {
23962306a36Sopenharmony_ci		*strength = 0;
24062306a36Sopenharmony_ci		goto error;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	*strength = ((tmp << 8) | tmp);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return ret;
24662306a36Sopenharmony_cierror:
24762306a36Sopenharmony_ci	dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
24862306a36Sopenharmony_ci	return ret;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	*snr = 0;
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	*ucblocks = 0;
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void ec100_release(struct dvb_frontend *fe)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct ec100_state *state = fe->demodulator_priv;
26662306a36Sopenharmony_ci	kfree(state);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic const struct dvb_frontend_ops ec100_ops;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistruct dvb_frontend *ec100_attach(const struct ec100_config *config,
27262306a36Sopenharmony_ci	struct i2c_adapter *i2c)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	int ret;
27562306a36Sopenharmony_ci	struct ec100_state *state = NULL;
27662306a36Sopenharmony_ci	u8 tmp;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* allocate memory for the internal state */
27962306a36Sopenharmony_ci	state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
28062306a36Sopenharmony_ci	if (state == NULL)
28162306a36Sopenharmony_ci		goto error;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* setup the state */
28462306a36Sopenharmony_ci	state->i2c = i2c;
28562306a36Sopenharmony_ci	memcpy(&state->config, config, sizeof(struct ec100_config));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* check if the demod is there */
28862306a36Sopenharmony_ci	ret = ec100_read_reg(state, 0x33, &tmp);
28962306a36Sopenharmony_ci	if (ret || tmp != 0x0b)
29062306a36Sopenharmony_ci		goto error;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* create dvb_frontend */
29362306a36Sopenharmony_ci	memcpy(&state->frontend.ops, &ec100_ops,
29462306a36Sopenharmony_ci		sizeof(struct dvb_frontend_ops));
29562306a36Sopenharmony_ci	state->frontend.demodulator_priv = state;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return &state->frontend;
29862306a36Sopenharmony_cierror:
29962306a36Sopenharmony_ci	kfree(state);
30062306a36Sopenharmony_ci	return NULL;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ec100_attach);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic const struct dvb_frontend_ops ec100_ops = {
30562306a36Sopenharmony_ci	.delsys = { SYS_DVBT },
30662306a36Sopenharmony_ci	.info = {
30762306a36Sopenharmony_ci		.name = "E3C EC100 DVB-T",
30862306a36Sopenharmony_ci		.caps =
30962306a36Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
31062306a36Sopenharmony_ci			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
31162306a36Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_QAM_16 |
31262306a36Sopenharmony_ci			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
31362306a36Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
31462306a36Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO |
31562306a36Sopenharmony_ci			FE_CAN_HIERARCHY_AUTO |
31662306a36Sopenharmony_ci			FE_CAN_MUTE_TS
31762306a36Sopenharmony_ci	},
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	.release = ec100_release,
32062306a36Sopenharmony_ci	.set_frontend = ec100_set_frontend,
32162306a36Sopenharmony_ci	.get_tune_settings = ec100_get_tune_settings,
32262306a36Sopenharmony_ci	.read_status = ec100_read_status,
32362306a36Sopenharmony_ci	.read_ber = ec100_read_ber,
32462306a36Sopenharmony_ci	.read_signal_strength = ec100_read_signal_strength,
32562306a36Sopenharmony_ci	.read_snr = ec100_read_snr,
32662306a36Sopenharmony_ci	.read_ucblocks = ec100_read_ucblocks,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
33062306a36Sopenharmony_ciMODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
33162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
332