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