Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6.git] / drivers / media / dvb / dvb-usb / cinergyT2-fe.c
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
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
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
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
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
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
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
247 static int cinergyt2_fe_init(struct dvb_frontend *fe)
248 {
249         return 0;
250 }
251
252 static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
253 {
254         deb_info("cinergyt2_fe_sleep() Called\n");
255         return 0;
256 }
257
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
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         case 8000000:
280                 param.bandwidth = 0;
281                 break;
282         case 7000000:
283                 param.bandwidth = 1;
284                 break;
285         case 6000000:
286                 param.bandwidth = 2;
287                 break;
288         }
289
290         err = dvb_usb_generic_rw(state->d,
291                         (char *)&param, sizeof(param),
292                         result, sizeof(result), 0);
293         if (err < 0)
294                 err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
295
296         return (err < 0) ? err : 0;
297 }
298
299 static void cinergyt2_fe_release(struct dvb_frontend *fe)
300 {
301         struct cinergyt2_fe_state *state = fe->demodulator_priv;
302         if (state != NULL)
303                 kfree(state);
304 }
305
306 static struct dvb_frontend_ops cinergyt2_fe_ops;
307
308 struct 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
322 static 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 };