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