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