File Index
Files and directories
Sign in
FILE

cinergyT2-fe.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/usb/dvb-usb/cinergyT2-fe.c C
  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};
~/ Read-only file preview.