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
43static 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
139struct cinergyt2_fe_state {
140	struct dvb_frontend fe;
141	struct dvb_usb_device *d;
142};
143
144static 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
177static 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
193static 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
211static 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
230static 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
247static int cinergyt2_fe_init(struct dvb_frontend *fe)
248{
249	return 0;
250}
251
252static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
253{
254	deb_info("cinergyt2_fe_sleep() Called\n");
255	return 0;
256}
257
258static 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
265static 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
300static void cinergyt2_fe_release(struct dvb_frontend *fe)
301{
302	struct cinergyt2_fe_state *state = fe->demodulator_priv;
303	kfree(state);
304}
305
306static struct dvb_frontend_ops cinergyt2_fe_ops;
307
308struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
309{
310	struct cinergyt2_fe_state *s = kzalloc(sizeof(
311					struct cinergyt2_fe_state), GFP_KERNEL);
312	if (s == NULL)
313		return NULL;
314
315	s->d = d;
316	memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
317	s->fe.demodulator_priv = s;
318	return &s->fe;
319}
320
321
322static struct dvb_frontend_ops cinergyt2_fe_ops = {
323	.delsys = { SYS_DVBT },
324	.info = {
325		.name			= DRIVER_NAME,
326		.frequency_min		= 174000000,
327		.frequency_max		= 862000000,
328		.frequency_stepsize	= 166667,
329		.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2
330			| FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
331			| FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8
332			| FE_CAN_FEC_AUTO | FE_CAN_QPSK
333			| FE_CAN_QAM_16 | FE_CAN_QAM_64
334			| FE_CAN_QAM_AUTO
335			| FE_CAN_TRANSMISSION_MODE_AUTO
336			| FE_CAN_GUARD_INTERVAL_AUTO
337			| FE_CAN_HIERARCHY_AUTO
338			| FE_CAN_RECOVER
339			| FE_CAN_MUTE_TS
340	},
341
342	.release		= cinergyt2_fe_release,
343
344	.init			= cinergyt2_fe_init,
345	.sleep			= cinergyt2_fe_sleep,
346
347	.set_frontend		= cinergyt2_fe_set_frontend,
348	.get_tune_settings	= cinergyt2_fe_get_tune_settings,
349
350	.read_status		= cinergyt2_fe_read_status,
351	.read_ber		= cinergyt2_fe_read_ber,
352	.read_signal_strength	= cinergyt2_fe_read_signal_strength,
353	.read_snr		= cinergyt2_fe_read_snr,
354	.read_ucblocks		= cinergyt2_fe_read_unc_blocks,
355};
356