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