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