Commit | Line | Data |
---|---|---|
9bbe076f | 1 | /* DVB USB compliant Linux driver for the |
458b634c | 2 | * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module |
9bbe076f | 3 | * |
458b634c AN |
4 | * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) |
5 | * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) | |
9bbe076f AN |
6 | * |
7 | * Thanks to GENPIX for the sample code used to implement this module. | |
8 | * | |
9 | * This module is based off the vp7045 and vp702x modules | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the Free | |
13 | * Software Foundation, version 2. | |
14 | * | |
15 | * see Documentation/dvb/README.dvb-usb for more information | |
16 | */ | |
17 | #include "gp8psk.h" | |
18 | ||
19 | struct gp8psk_fe_state { | |
20 | struct dvb_frontend fe; | |
9bbe076f | 21 | struct dvb_usb_device *d; |
458b634c | 22 | u8 lock; |
9bbe076f | 23 | u16 snr; |
458b634c AN |
24 | unsigned long next_status_check; |
25 | unsigned long status_check_interval; | |
9bbe076f AN |
26 | }; |
27 | ||
02ebf23b AN |
28 | static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) |
29 | { | |
30 | struct gp8psk_fe_state *st = fe->demodulator_priv; | |
31 | u8 status; | |
32 | gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); | |
33 | return status & bmDCtuned; | |
34 | } | |
35 | ||
36 | static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) | |
37 | { | |
38 | struct gp8psk_fe_state *state = fe->demodulator_priv; | |
39 | return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); | |
40 | } | |
41 | ||
458b634c AN |
42 | static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) |
43 | { | |
44 | u8 buf[6]; | |
45 | if (time_after(jiffies,st->next_status_check)) { | |
46 | gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); | |
47 | gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); | |
48 | st->snr = (buf[1]) << 8 | buf[0]; | |
49 | st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; | |
50 | } | |
51 | return 0; | |
52 | } | |
53 | ||
0df289a2 MCC |
54 | static int gp8psk_fe_read_status(struct dvb_frontend *fe, |
55 | enum fe_status *status) | |
9bbe076f AN |
56 | { |
57 | struct gp8psk_fe_state *st = fe->demodulator_priv; | |
458b634c | 58 | gp8psk_fe_update_status(st); |
9bbe076f | 59 | |
458b634c | 60 | if (st->lock) |
9bbe076f AN |
61 | *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; |
62 | else | |
63 | *status = 0; | |
64 | ||
458b634c AN |
65 | if (*status & FE_HAS_LOCK) |
66 | st->status_check_interval = 1000; | |
67 | else | |
68 | st->status_check_interval = 100; | |
9bbe076f AN |
69 | return 0; |
70 | } | |
71 | ||
72 | /* not supported by this Frontend */ | |
73 | static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) | |
74 | { | |
75 | (void) fe; | |
76 | *ber = 0; | |
77 | return 0; | |
78 | } | |
79 | ||
80 | /* not supported by this Frontend */ | |
81 | static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) | |
82 | { | |
83 | (void) fe; | |
84 | *unc = 0; | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) | |
89 | { | |
90 | struct gp8psk_fe_state *st = fe->demodulator_priv; | |
458b634c AN |
91 | gp8psk_fe_update_status(st); |
92 | /* snr is reported in dBu*256 */ | |
93 | *snr = st->snr; | |
9bbe076f AN |
94 | return 0; |
95 | } | |
96 | ||
97 | static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) | |
98 | { | |
458b634c AN |
99 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
100 | gp8psk_fe_update_status(st); | |
101 | /* snr is reported in dBu*256 */ | |
102 | /* snr / 38.4 ~= 100% strength */ | |
103 | /* snr * 17 returns 100% strength as 65535 */ | |
104 | if (st->snr > 0xf00) | |
105 | *strength = 0xffff; | |
106 | else | |
107 | *strength = (st->snr << 4) + st->snr; /* snr*17 */ | |
108 | return 0; | |
9bbe076f AN |
109 | } |
110 | ||
111 | static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) | |
112 | { | |
fb249ca6 | 113 | tune->min_delay_ms = 800; |
9bbe076f AN |
114 | return 0; |
115 | } | |
116 | ||
6e07d5c5 | 117 | static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) |
9bbe076f AN |
118 | { |
119 | struct gp8psk_fe_state *state = fe->demodulator_priv; | |
02ebf23b | 120 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
9bbe076f | 121 | u8 cmd[10]; |
6e07d5c5 | 122 | u32 freq = c->frequency * 1000; |
02ebf23b AN |
123 | int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); |
124 | ||
125 | deb_fe("%s()\n", __func__); | |
9bbe076f AN |
126 | |
127 | cmd[4] = freq & 0xff; | |
128 | cmd[5] = (freq >> 8) & 0xff; | |
129 | cmd[6] = (freq >> 16) & 0xff; | |
130 | cmd[7] = (freq >> 24) & 0xff; | |
131 | ||
8fc74fdb AO |
132 | /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ |
133 | if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) | |
134 | c->delivery_system = SYS_TURBO; | |
135 | ||
02ebf23b AN |
136 | switch (c->delivery_system) { |
137 | case SYS_DVBS: | |
8fc74fdb | 138 | if (c->modulation != QPSK) { |
02ebf23b AN |
139 | deb_fe("%s: unsupported modulation selected (%d)\n", |
140 | __func__, c->modulation); | |
141 | return -EOPNOTSUPP; | |
142 | } | |
143 | c->fec_inner = FEC_AUTO; | |
9bbe076f | 144 | break; |
8fc74fdb | 145 | case SYS_DVBS2: /* kept for backwards compatibility */ |
02ebf23b AN |
146 | deb_fe("%s: DVB-S2 delivery system selected\n", __func__); |
147 | break; | |
8fc74fdb AO |
148 | case SYS_TURBO: |
149 | deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); | |
150 | break; | |
02ebf23b | 151 | |
9bbe076f | 152 | default: |
02ebf23b AN |
153 | deb_fe("%s: unsupported delivery system selected (%d)\n", |
154 | __func__, c->delivery_system); | |
155 | return -EOPNOTSUPP; | |
156 | } | |
157 | ||
158 | cmd[0] = c->symbol_rate & 0xff; | |
159 | cmd[1] = (c->symbol_rate >> 8) & 0xff; | |
160 | cmd[2] = (c->symbol_rate >> 16) & 0xff; | |
161 | cmd[3] = (c->symbol_rate >> 24) & 0xff; | |
162 | switch (c->modulation) { | |
163 | case QPSK: | |
164 | if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) | |
165 | if (gp8psk_tuned_to_DCII(fe)) | |
166 | gp8psk_bcm4500_reload(state->d); | |
167 | switch (c->fec_inner) { | |
168 | case FEC_1_2: | |
169 | cmd[9] = 0; break; | |
170 | case FEC_2_3: | |
171 | cmd[9] = 1; break; | |
172 | case FEC_3_4: | |
173 | cmd[9] = 2; break; | |
174 | case FEC_5_6: | |
175 | cmd[9] = 3; break; | |
176 | case FEC_7_8: | |
177 | cmd[9] = 4; break; | |
178 | case FEC_AUTO: | |
179 | cmd[9] = 5; break; | |
180 | default: | |
181 | cmd[9] = 5; break; | |
182 | } | |
8fc74fdb AO |
183 | if (c->delivery_system == SYS_TURBO) |
184 | cmd[8] = ADV_MOD_TURBO_QPSK; | |
185 | else | |
186 | cmd[8] = ADV_MOD_DVB_QPSK; | |
02ebf23b AN |
187 | break; |
188 | case PSK_8: /* PSK_8 is for compatibility with DN */ | |
189 | cmd[8] = ADV_MOD_TURBO_8PSK; | |
190 | switch (c->fec_inner) { | |
191 | case FEC_2_3: | |
192 | cmd[9] = 0; break; | |
193 | case FEC_3_4: | |
194 | cmd[9] = 1; break; | |
195 | case FEC_3_5: | |
196 | cmd[9] = 2; break; | |
197 | case FEC_5_6: | |
198 | cmd[9] = 3; break; | |
199 | case FEC_8_9: | |
200 | cmd[9] = 4; break; | |
201 | default: | |
202 | cmd[9] = 0; break; | |
203 | } | |
204 | break; | |
205 | case QAM_16: /* QAM_16 is for compatibility with DN */ | |
206 | cmd[8] = ADV_MOD_TURBO_16QAM; | |
9bbe076f AN |
207 | cmd[9] = 0; |
208 | break; | |
02ebf23b AN |
209 | default: /* Unknown modulation */ |
210 | deb_fe("%s: unsupported modulation selected (%d)\n", | |
211 | __func__, c->modulation); | |
212 | return -EOPNOTSUPP; | |
9bbe076f AN |
213 | } |
214 | ||
02ebf23b AN |
215 | if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) |
216 | gp8psk_set_tuner_mode(fe, 0); | |
217 | gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); | |
9bbe076f | 218 | |
458b634c AN |
219 | state->lock = 0; |
220 | state->next_status_check = jiffies; | |
221 | state->status_check_interval = 200; | |
9bbe076f AN |
222 | |
223 | return 0; | |
224 | } | |
225 | ||
9bbe076f AN |
226 | static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, |
227 | struct dvb_diseqc_master_cmd *m) | |
228 | { | |
229 | struct gp8psk_fe_state *st = fe->demodulator_priv; | |
230 | ||
708bebdd | 231 | deb_fe("%s\n",__func__); |
9bbe076f AN |
232 | |
233 | if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, | |
234 | m->msg, m->msg_len)) { | |
235 | return -EINVAL; | |
236 | } | |
237 | return 0; | |
238 | } | |
239 | ||
0df289a2 MCC |
240 | static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, |
241 | enum fe_sec_mini_cmd burst) | |
9bbe076f AN |
242 | { |
243 | struct gp8psk_fe_state *st = fe->demodulator_priv; | |
244 | u8 cmd; | |
245 | ||
708bebdd | 246 | deb_fe("%s\n",__func__); |
9bbe076f AN |
247 | |
248 | /* These commands are certainly wrong */ | |
249 | cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; | |
250 | ||
251 | if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, | |
252 | &cmd, 0)) { | |
253 | return -EINVAL; | |
254 | } | |
255 | return 0; | |
256 | } | |
257 | ||
0df289a2 MCC |
258 | static int gp8psk_fe_set_tone(struct dvb_frontend *fe, |
259 | enum fe_sec_tone_mode tone) | |
9bbe076f AN |
260 | { |
261 | struct gp8psk_fe_state* state = fe->demodulator_priv; | |
262 | ||
263 | if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, | |
264 | (tone == SEC_TONE_ON), 0, NULL, 0)) { | |
265 | return -EINVAL; | |
266 | } | |
267 | return 0; | |
268 | } | |
269 | ||
0df289a2 MCC |
270 | static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, |
271 | enum fe_sec_voltage voltage) | |
9bbe076f AN |
272 | { |
273 | struct gp8psk_fe_state* state = fe->demodulator_priv; | |
274 | ||
275 | if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, | |
276 | voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { | |
277 | return -EINVAL; | |
278 | } | |
279 | return 0; | |
280 | } | |
281 | ||
458b634c AN |
282 | static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) |
283 | { | |
284 | struct gp8psk_fe_state* state = fe->demodulator_priv; | |
285 | return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); | |
286 | } | |
287 | ||
9bbe076f AN |
288 | static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) |
289 | { | |
290 | struct gp8psk_fe_state* state = fe->demodulator_priv; | |
291 | u8 cmd = sw_cmd & 0x7f; | |
292 | ||
293 | if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, | |
294 | NULL, 0)) { | |
295 | return -EINVAL; | |
296 | } | |
297 | if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), | |
298 | 0, NULL, 0)) { | |
299 | return -EINVAL; | |
300 | } | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static void gp8psk_fe_release(struct dvb_frontend* fe) | |
306 | { | |
307 | struct gp8psk_fe_state *state = fe->demodulator_priv; | |
308 | kfree(state); | |
309 | } | |
310 | ||
311 | static struct dvb_frontend_ops gp8psk_fe_ops; | |
312 | ||
313 | struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) | |
314 | { | |
315 | struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); | |
316 | if (s == NULL) | |
317 | goto error; | |
318 | ||
319 | s->d = d; | |
320 | memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); | |
321 | s->fe.demodulator_priv = s; | |
322 | ||
323 | goto success; | |
324 | error: | |
325 | return NULL; | |
326 | success: | |
327 | return &s->fe; | |
328 | } | |
329 | ||
330 | ||
331 | static struct dvb_frontend_ops gp8psk_fe_ops = { | |
6e07d5c5 | 332 | .delsys = { SYS_DVBS }, |
9bbe076f | 333 | .info = { |
d12da8e9 | 334 | .name = "Genpix DVB-S", |
458b634c AN |
335 | .frequency_min = 800000, |
336 | .frequency_max = 2250000, | |
9bbe076f AN |
337 | .frequency_stepsize = 100, |
338 | .symbol_rate_min = 1000000, | |
339 | .symbol_rate_max = 45000000, | |
340 | .symbol_rate_tolerance = 500, /* ppm */ | |
341 | .caps = FE_CAN_INVERSION_AUTO | | |
02ebf23b AN |
342 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
343 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | |
344 | /* | |
345 | * FE_CAN_QAM_16 is for compatibility | |
346 | * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) | |
347 | */ | |
f6a20eb1 | 348 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC |
9bbe076f AN |
349 | }, |
350 | ||
351 | .release = gp8psk_fe_release, | |
352 | ||
353 | .init = NULL, | |
354 | .sleep = NULL, | |
355 | ||
6e07d5c5 | 356 | .set_frontend = gp8psk_fe_set_frontend, |
02ebf23b | 357 | |
9bbe076f AN |
358 | .get_tune_settings = gp8psk_fe_get_tune_settings, |
359 | ||
360 | .read_status = gp8psk_fe_read_status, | |
361 | .read_ber = gp8psk_fe_read_ber, | |
362 | .read_signal_strength = gp8psk_fe_read_signal_strength, | |
363 | .read_snr = gp8psk_fe_read_snr, | |
364 | .read_ucblocks = gp8psk_fe_read_unc_blocks, | |
365 | ||
366 | .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, | |
367 | .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, | |
368 | .set_tone = gp8psk_fe_set_tone, | |
369 | .set_voltage = gp8psk_fe_set_voltage, | |
370 | .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, | |
458b634c | 371 | .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage |
9bbe076f | 372 | }; |