1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Frontend driver for the GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module |
4 | * |
5 | * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) |
6 | * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) |
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 |
11 | */ |
12 | |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | |
15 | #include "gp8psk-fe.h" |
16 | #include <media/dvb_frontend.h> |
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) |
27 | |
28 | struct gp8psk_fe_state { |
29 | struct dvb_frontend fe; |
30 | void *priv; |
31 | const struct gp8psk_fe_ops *ops; |
32 | bool is_rev1; |
33 | u8 lock; |
34 | u16 snr; |
35 | unsigned long next_status_check; |
36 | unsigned long status_check_interval; |
37 | }; |
38 | |
39 | static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) |
40 | { |
41 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
42 | u8 status; |
43 | |
44 | st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1); |
45 | return status & bmDCtuned; |
46 | } |
47 | |
48 | static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) |
49 | { |
50 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
51 | |
52 | return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0); |
53 | } |
54 | |
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)) { |
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); |
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 | |
67 | static int gp8psk_fe_read_status(struct dvb_frontend *fe, |
68 | enum fe_status *status) |
69 | { |
70 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
71 | gp8psk_fe_update_status(st); |
72 | |
73 | if (st->lock) |
74 | *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; |
75 | else |
76 | *status = 0; |
77 | |
78 | if (*status & FE_HAS_LOCK) |
79 | st->status_check_interval = 1000; |
80 | else |
81 | st->status_check_interval = 100; |
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; |
104 | gp8psk_fe_update_status(st); |
105 | /* snr is reported in dBu*256 */ |
106 | *snr = st->snr; |
107 | return 0; |
108 | } |
109 | |
110 | static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) |
111 | { |
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; |
122 | } |
123 | |
124 | static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) |
125 | { |
126 | tune->min_delay_ms = 800; |
127 | return 0; |
128 | } |
129 | |
130 | static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) |
131 | { |
132 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
133 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
134 | u8 cmd[10]; |
135 | u32 freq = c->frequency * 1000; |
136 | |
137 | dprintk("%s()\n" , __func__); |
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 | |
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 | |
148 | switch (c->delivery_system) { |
149 | case SYS_DVBS: |
150 | if (c->modulation != QPSK) { |
151 | dprintk("%s: unsupported modulation selected (%d)\n" , |
152 | __func__, c->modulation); |
153 | return -EOPNOTSUPP; |
154 | } |
155 | c->fec_inner = FEC_AUTO; |
156 | break; |
157 | case SYS_DVBS2: /* kept for backwards compatibility */ |
158 | dprintk("%s: DVB-S2 delivery system selected\n" , __func__); |
159 | break; |
160 | case SYS_TURBO: |
161 | dprintk("%s: Turbo-FEC delivery system selected\n" , __func__); |
162 | break; |
163 | |
164 | default: |
165 | dprintk("%s: unsupported delivery system selected (%d)\n" , |
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: |
176 | if (st->is_rev1) |
177 | if (gp8psk_tuned_to_DCII(fe)) |
178 | st->ops->reload(st->priv); |
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 | } |
195 | if (c->delivery_system == SYS_TURBO) |
196 | cmd[8] = ADV_MOD_TURBO_QPSK; |
197 | else |
198 | cmd[8] = ADV_MOD_DVB_QPSK; |
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; |
219 | cmd[9] = 0; |
220 | break; |
221 | default: /* Unknown modulation */ |
222 | dprintk("%s: unsupported modulation selected (%d)\n" , |
223 | __func__, c->modulation); |
224 | return -EOPNOTSUPP; |
225 | } |
226 | |
227 | if (st->is_rev1) |
228 | gp8psk_set_tuner_mode(fe, mode: 0); |
229 | st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10); |
230 | |
231 | st->lock = 0; |
232 | st->next_status_check = jiffies; |
233 | st->status_check_interval = 200; |
234 | |
235 | return 0; |
236 | } |
237 | |
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 | |
243 | dprintk("%s\n" , __func__); |
244 | |
245 | if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0, |
246 | m->msg, m->msg_len)) { |
247 | return -EINVAL; |
248 | } |
249 | return 0; |
250 | } |
251 | |
252 | static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe, |
253 | enum fe_sec_mini_cmd burst) |
254 | { |
255 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
256 | u8 cmd; |
257 | |
258 | dprintk("%s\n" , __func__); |
259 | |
260 | /* These commands are certainly wrong */ |
261 | cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; |
262 | |
263 | if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0, |
264 | &cmd, 0)) { |
265 | return -EINVAL; |
266 | } |
267 | return 0; |
268 | } |
269 | |
270 | static int gp8psk_fe_set_tone(struct dvb_frontend *fe, |
271 | enum fe_sec_tone_mode tone) |
272 | { |
273 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
274 | |
275 | if (st->ops->out(st->priv, SET_22KHZ_TONE, |
276 | (tone == SEC_TONE_ON), 0, NULL, 0)) { |
277 | return -EINVAL; |
278 | } |
279 | return 0; |
280 | } |
281 | |
282 | static int gp8psk_fe_set_voltage(struct dvb_frontend *fe, |
283 | enum fe_sec_voltage voltage) |
284 | { |
285 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
286 | |
287 | if (st->ops->out(st->priv, SET_LNB_VOLTAGE, |
288 | voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { |
289 | return -EINVAL; |
290 | } |
291 | return 0; |
292 | } |
293 | |
294 | static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) |
295 | { |
296 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
297 | |
298 | return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0); |
299 | } |
300 | |
301 | static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) |
302 | { |
303 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
304 | u8 cmd = sw_cmd & 0x7f; |
305 | |
306 | if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0)) |
307 | return -EINVAL; |
308 | |
309 | if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), |
310 | 0, NULL, 0)) |
311 | return -EINVAL; |
312 | |
313 | return 0; |
314 | } |
315 | |
316 | static void gp8psk_fe_release(struct dvb_frontend* fe) |
317 | { |
318 | struct gp8psk_fe_state *st = fe->demodulator_priv; |
319 | |
320 | kfree(objp: st); |
321 | } |
322 | |
323 | static const struct dvb_frontend_ops gp8psk_fe_ops; |
324 | |
325 | struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops, |
326 | void *priv, bool is_rev1) |
327 | { |
328 | struct gp8psk_fe_state *st; |
329 | |
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(size: 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); |
350 | |
351 | static const struct dvb_frontend_ops gp8psk_fe_ops = { |
352 | .delsys = { SYS_DVBS }, |
353 | .info = { |
354 | .name = "Genpix DVB-S" , |
355 | .frequency_min_hz = 800 * MHz, |
356 | .frequency_max_hz = 2250 * MHz, |
357 | .frequency_stepsize_hz = 100 * kHz, |
358 | .symbol_rate_min = 1000000, |
359 | .symbol_rate_max = 45000000, |
360 | .symbol_rate_tolerance = 500, /* ppm */ |
361 | .caps = FE_CAN_INVERSION_AUTO | |
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 | */ |
368 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC |
369 | }, |
370 | |
371 | .release = gp8psk_fe_release, |
372 | |
373 | .init = NULL, |
374 | .sleep = NULL, |
375 | |
376 | .set_frontend = gp8psk_fe_set_frontend, |
377 | |
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, |
391 | .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage |
392 | }; |
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" ); |
398 | |