1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
4 *
5 * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
6 *
7 * Based on the dvb-usb-framework code and the
8 * original Terratec Cinergy T2 driver by:
9 *
10 * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
11 *                  Holger Waechtler <holger@qanu.de>
12 *
13 *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
14 */
15
16#include "cinergyT2.h"
17
18
19/*
20 *  convert linux-dvb frontend parameter set into TPS.
21 *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
22 *
23 *  This function is probably reusable and may better get placed in a support
24 *  library.
25 *
26 *  We replace erroneous fields by default TPS fields (the ones with value 0).
27 */
28
29static uint16_t compute_tps(struct dtv_frontend_properties *op)
30{
31	uint16_t tps = 0;
32
33	switch (op->code_rate_HP) {
34	case FEC_2_3:
35		tps |= (1 << 7);
36		break;
37	case FEC_3_4:
38		tps |= (2 << 7);
39		break;
40	case FEC_5_6:
41		tps |= (3 << 7);
42		break;
43	case FEC_7_8:
44		tps |= (4 << 7);
45		break;
46	case FEC_1_2:
47	case FEC_AUTO:
48	default:
49		/* tps |= (0 << 7) */;
50	}
51
52	switch (op->code_rate_LP) {
53	case FEC_2_3:
54		tps |= (1 << 4);
55		break;
56	case FEC_3_4:
57		tps |= (2 << 4);
58		break;
59	case FEC_5_6:
60		tps |= (3 << 4);
61		break;
62	case FEC_7_8:
63		tps |= (4 << 4);
64		break;
65	case FEC_1_2:
66	case FEC_AUTO:
67	default:
68		/* tps |= (0 << 4) */;
69	}
70
71	switch (op->modulation) {
72	case QAM_16:
73		tps |= (1 << 13);
74		break;
75	case QAM_64:
76		tps |= (2 << 13);
77		break;
78	case QPSK:
79	default:
80		/* tps |= (0 << 13) */;
81	}
82
83	switch (op->transmission_mode) {
84	case TRANSMISSION_MODE_8K:
85		tps |= (1 << 0);
86		break;
87	case TRANSMISSION_MODE_2K:
88	default:
89		/* tps |= (0 << 0) */;
90	}
91
92	switch (op->guard_interval) {
93	case GUARD_INTERVAL_1_16:
94		tps |= (1 << 2);
95		break;
96	case GUARD_INTERVAL_1_8:
97		tps |= (2 << 2);
98		break;
99	case GUARD_INTERVAL_1_4:
100		tps |= (3 << 2);
101		break;
102	case GUARD_INTERVAL_1_32:
103	default:
104		/* tps |= (0 << 2) */;
105	}
106
107	switch (op->hierarchy) {
108	case HIERARCHY_1:
109		tps |= (1 << 10);
110		break;
111	case HIERARCHY_2:
112		tps |= (2 << 10);
113		break;
114	case HIERARCHY_4:
115		tps |= (3 << 10);
116		break;
117	case HIERARCHY_NONE:
118	default:
119		/* tps |= (0 << 10) */;
120	}
121
122	return tps;
123}
124
125struct cinergyt2_fe_state {
126	struct dvb_frontend fe;
127	struct dvb_usb_device *d;
128
129	unsigned char data[64];
130	struct mutex data_mutex;
131
132	struct dvbt_get_status_msg status;
133};
134
135static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
136				    enum fe_status *status)
137{
138	struct cinergyt2_fe_state *state = fe->demodulator_priv;
139	int ret;
140
141	mutex_lock(&state->data_mutex);
142	state->data[0] = CINERGYT2_EP1_GET_TUNER_STATUS;
143
144	ret = dvb_usb_generic_rw(state->d, state->data, 1,
145				 state->data, sizeof(state->status), 0);
146	if (!ret)
147		memcpy(&state->status, state->data, sizeof(state->status));
148	mutex_unlock(&state->data_mutex);
149
150	if (ret < 0)
151		return ret;
152
153	*status = 0;
154
155	if (0xffff - le16_to_cpu(state->status.gain) > 30)
156		*status |= FE_HAS_SIGNAL;
157	if (state->status.lock_bits & (1 << 6))
158		*status |= FE_HAS_LOCK;
159	if (state->status.lock_bits & (1 << 5))
160		*status |= FE_HAS_SYNC;
161	if (state->status.lock_bits & (1 << 4))
162		*status |= FE_HAS_CARRIER;
163	if (state->status.lock_bits & (1 << 1))
164		*status |= FE_HAS_VITERBI;
165
166	if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
167			(FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
168		*status &= ~FE_HAS_LOCK;
169
170	return 0;
171}
172
173static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
174{
175	struct cinergyt2_fe_state *state = fe->demodulator_priv;
176
177	*ber = le32_to_cpu(state->status.viterbi_error_rate);
178	return 0;
179}
180
181static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
182{
183	struct cinergyt2_fe_state *state = fe->demodulator_priv;
184
185	*unc = le32_to_cpu(state->status.uncorrected_block_count);
186	return 0;
187}
188
189static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe,
190						u16 *strength)
191{
192	struct cinergyt2_fe_state *state = fe->demodulator_priv;
193
194	*strength = (0xffff - le16_to_cpu(state->status.gain));
195	return 0;
196}
197
198static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
199{
200	struct cinergyt2_fe_state *state = fe->demodulator_priv;
201
202	*snr = (state->status.snr << 8) | state->status.snr;
203	return 0;
204}
205
206static int cinergyt2_fe_init(struct dvb_frontend *fe)
207{
208	return 0;
209}
210
211static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
212{
213	deb_info("cinergyt2_fe_sleep() Called\n");
214	return 0;
215}
216
217static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe,
218				struct dvb_frontend_tune_settings *tune)
219{
220	tune->min_delay_ms = 800;
221	return 0;
222}
223
224static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe)
225{
226	struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
227	struct cinergyt2_fe_state *state = fe->demodulator_priv;
228	struct dvbt_set_parameters_msg *param;
229	int err;
230
231	mutex_lock(&state->data_mutex);
232
233	param = (void *)state->data;
234	param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
235	param->tps = cpu_to_le16(compute_tps(fep));
236	param->freq = cpu_to_le32(fep->frequency / 1000);
237	param->flags = 0;
238
239	switch (fep->bandwidth_hz) {
240	default:
241	case 8000000:
242		param->bandwidth = 8;
243		break;
244	case 7000000:
245		param->bandwidth = 7;
246		break;
247	case 6000000:
248		param->bandwidth = 6;
249		break;
250	}
251
252	err = dvb_usb_generic_rw(state->d, state->data, sizeof(*param),
253				 state->data, 2, 0);
254	if (err < 0)
255		err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
256
257	mutex_unlock(&state->data_mutex);
258	return (err < 0) ? err : 0;
259}
260
261static void cinergyt2_fe_release(struct dvb_frontend *fe)
262{
263	struct cinergyt2_fe_state *state = fe->demodulator_priv;
264	kfree(state);
265}
266
267static const struct dvb_frontend_ops cinergyt2_fe_ops;
268
269struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
270{
271	struct cinergyt2_fe_state *s = kzalloc(sizeof(
272					struct cinergyt2_fe_state), GFP_KERNEL);
273	if (s == NULL)
274		return NULL;
275
276	s->d = d;
277	memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
278	s->fe.demodulator_priv = s;
279	mutex_init(&s->data_mutex);
280	return &s->fe;
281}
282
283
284static const struct dvb_frontend_ops cinergyt2_fe_ops = {
285	.delsys = { SYS_DVBT },
286	.info = {
287		.name			= DRIVER_NAME,
288		.frequency_min_hz	= 174 * MHz,
289		.frequency_max_hz	= 862 * MHz,
290		.frequency_stepsize_hz	= 166667,
291		.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2
292			| FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
293			| FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8
294			| FE_CAN_FEC_AUTO | FE_CAN_QPSK
295			| FE_CAN_QAM_16 | FE_CAN_QAM_64
296			| FE_CAN_QAM_AUTO
297			| FE_CAN_TRANSMISSION_MODE_AUTO
298			| FE_CAN_GUARD_INTERVAL_AUTO
299			| FE_CAN_HIERARCHY_AUTO
300			| FE_CAN_RECOVER
301			| FE_CAN_MUTE_TS
302	},
303
304	.release		= cinergyt2_fe_release,
305
306	.init			= cinergyt2_fe_init,
307	.sleep			= cinergyt2_fe_sleep,
308
309	.set_frontend		= cinergyt2_fe_set_frontend,
310	.get_tune_settings	= cinergyt2_fe_get_tune_settings,
311
312	.read_status		= cinergyt2_fe_read_status,
313	.read_ber		= cinergyt2_fe_read_ber,
314	.read_signal_strength	= cinergyt2_fe_read_signal_strength,
315	.read_snr		= cinergyt2_fe_read_snr,
316	.read_ucblocks		= cinergyt2_fe_read_unc_blocks,
317};
318