1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* DVB frontend part of the Linux driver for the TwinhanDTV StarBox USB2.0 |
3 | * DVB-S receiver. |
4 | * |
5 | * Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de> |
6 | * Metzler Brothers Systementwicklung GbR |
7 | * |
8 | * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de> |
9 | * |
10 | * Thanks to Twinhan who kindly provided hardware and information. |
11 | * |
12 | * This file can be removed soon, after the DST-driver is rewritten to provice |
13 | * the frontend-controlling separately. |
14 | * |
15 | * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information |
16 | */ |
17 | #include "vp702x.h" |
18 | |
19 | struct vp702x_fe_state { |
20 | struct dvb_frontend fe; |
21 | struct dvb_usb_device *d; |
22 | |
23 | struct dvb_frontend_ops ops; |
24 | |
25 | enum fe_sec_voltage voltage; |
26 | enum fe_sec_tone_mode tone_mode; |
27 | |
28 | u8 lnb_buf[8]; |
29 | |
30 | u8 lock; |
31 | u8 sig; |
32 | u8 snr; |
33 | |
34 | unsigned long next_status_check; |
35 | unsigned long status_check_interval; |
36 | }; |
37 | |
38 | static int vp702x_fe_refresh_state(struct vp702x_fe_state *st) |
39 | { |
40 | struct vp702x_device_state *dst = st->d->priv; |
41 | u8 *buf; |
42 | |
43 | if (time_after(jiffies, st->next_status_check)) { |
44 | mutex_lock(&dst->buf_mutex); |
45 | buf = dst->buf; |
46 | |
47 | vp702x_usb_in_op(d: st->d, READ_STATUS, value: 0, index: 0, b: buf, blen: 10); |
48 | st->lock = buf[4]; |
49 | |
50 | vp702x_usb_in_op(d: st->d, READ_TUNER_REG_REQ, value: 0x11, index: 0, b: buf, blen: 1); |
51 | st->snr = buf[0]; |
52 | |
53 | vp702x_usb_in_op(d: st->d, READ_TUNER_REG_REQ, value: 0x15, index: 0, b: buf, blen: 1); |
54 | st->sig = buf[0]; |
55 | |
56 | mutex_unlock(lock: &dst->buf_mutex); |
57 | st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; |
58 | } |
59 | return 0; |
60 | } |
61 | |
62 | static u8 vp702x_chksum(u8 *buf,int f, int count) |
63 | { |
64 | u8 s = 0; |
65 | int i; |
66 | for (i = f; i < f+count; i++) |
67 | s += buf[i]; |
68 | return ~s+1; |
69 | } |
70 | |
71 | static int vp702x_fe_read_status(struct dvb_frontend *fe, |
72 | enum fe_status *status) |
73 | { |
74 | struct vp702x_fe_state *st = fe->demodulator_priv; |
75 | vp702x_fe_refresh_state(st); |
76 | deb_fe("%s\n" ,__func__); |
77 | |
78 | if (st->lock == 0) |
79 | *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; |
80 | else |
81 | *status = 0; |
82 | |
83 | if (*status & FE_HAS_LOCK) |
84 | st->status_check_interval = 1000; |
85 | else |
86 | st->status_check_interval = 250; |
87 | return 0; |
88 | } |
89 | |
90 | /* not supported by this Frontend */ |
91 | static int vp702x_fe_read_ber(struct dvb_frontend* fe, u32 *ber) |
92 | { |
93 | struct vp702x_fe_state *st = fe->demodulator_priv; |
94 | vp702x_fe_refresh_state(st); |
95 | *ber = 0; |
96 | return 0; |
97 | } |
98 | |
99 | /* not supported by this Frontend */ |
100 | static int vp702x_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) |
101 | { |
102 | struct vp702x_fe_state *st = fe->demodulator_priv; |
103 | vp702x_fe_refresh_state(st); |
104 | *unc = 0; |
105 | return 0; |
106 | } |
107 | |
108 | static int vp702x_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) |
109 | { |
110 | struct vp702x_fe_state *st = fe->demodulator_priv; |
111 | vp702x_fe_refresh_state(st); |
112 | |
113 | *strength = (st->sig << 8) | st->sig; |
114 | return 0; |
115 | } |
116 | |
117 | static int vp702x_fe_read_snr(struct dvb_frontend* fe, u16 *snr) |
118 | { |
119 | u8 _snr; |
120 | struct vp702x_fe_state *st = fe->demodulator_priv; |
121 | vp702x_fe_refresh_state(st); |
122 | |
123 | _snr = (st->snr & 0x1f) * 0xff / 0x1f; |
124 | *snr = (_snr << 8) | _snr; |
125 | return 0; |
126 | } |
127 | |
128 | static int vp702x_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) |
129 | { |
130 | deb_fe("%s\n" ,__func__); |
131 | tune->min_delay_ms = 2000; |
132 | return 0; |
133 | } |
134 | |
135 | static int vp702x_fe_set_frontend(struct dvb_frontend *fe) |
136 | { |
137 | struct dtv_frontend_properties *fep = &fe->dtv_property_cache; |
138 | struct vp702x_fe_state *st = fe->demodulator_priv; |
139 | struct vp702x_device_state *dst = st->d->priv; |
140 | u32 freq = fep->frequency/1000; |
141 | /*CalFrequency*/ |
142 | /* u16 frequencyRef[16] = { 2, 4, 8, 16, 32, 64, 128, 256, 24, 5, 10, 20, 40, 80, 160, 320 }; */ |
143 | u64 sr; |
144 | u8 *cmd; |
145 | |
146 | mutex_lock(&dst->buf_mutex); |
147 | |
148 | cmd = dst->buf; |
149 | memset(cmd, 0, 10); |
150 | |
151 | cmd[0] = (freq >> 8) & 0x7f; |
152 | cmd[1] = freq & 0xff; |
153 | cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */ |
154 | |
155 | sr = (u64) (fep->symbol_rate/1000) << 20; |
156 | do_div(sr,88000); |
157 | cmd[3] = (sr >> 12) & 0xff; |
158 | cmd[4] = (sr >> 4) & 0xff; |
159 | cmd[5] = (sr << 4) & 0xf0; |
160 | |
161 | deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n" , |
162 | fep->frequency, freq, freq, fep->symbol_rate, |
163 | (unsigned long) sr, (unsigned long) sr); |
164 | |
165 | /* if (fep->inversion == INVERSION_ON) |
166 | cmd[6] |= 0x80; */ |
167 | |
168 | if (st->voltage == SEC_VOLTAGE_18) |
169 | cmd[6] |= 0x40; |
170 | |
171 | /* if (fep->symbol_rate > 8000000) |
172 | cmd[6] |= 0x20; |
173 | |
174 | if (fep->frequency < 1531000) |
175 | cmd[6] |= 0x04; |
176 | |
177 | if (st->tone_mode == SEC_TONE_ON) |
178 | cmd[6] |= 0x01;*/ |
179 | |
180 | cmd[7] = vp702x_chksum(buf: cmd,f: 0,count: 7); |
181 | |
182 | st->status_check_interval = 250; |
183 | st->next_status_check = jiffies; |
184 | |
185 | vp702x_usb_inout_op(d: st->d, o: cmd, olen: 8, i: cmd, ilen: 10, msec: 100); |
186 | |
187 | if (cmd[2] == 0 && cmd[3] == 0) |
188 | deb_fe("tuning failed.\n" ); |
189 | else |
190 | deb_fe("tuning succeeded.\n" ); |
191 | |
192 | mutex_unlock(lock: &dst->buf_mutex); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static int vp702x_fe_init(struct dvb_frontend *fe) |
198 | { |
199 | struct vp702x_fe_state *st = fe->demodulator_priv; |
200 | deb_fe("%s\n" ,__func__); |
201 | vp702x_usb_in_op(d: st->d, RESET_TUNER, value: 0, index: 0, NULL, blen: 0); |
202 | return 0; |
203 | } |
204 | |
205 | static int vp702x_fe_sleep(struct dvb_frontend *fe) |
206 | { |
207 | deb_fe("%s\n" ,__func__); |
208 | return 0; |
209 | } |
210 | |
211 | static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe, |
212 | struct dvb_diseqc_master_cmd *m) |
213 | { |
214 | u8 *cmd; |
215 | struct vp702x_fe_state *st = fe->demodulator_priv; |
216 | struct vp702x_device_state *dst = st->d->priv; |
217 | |
218 | deb_fe("%s\n" ,__func__); |
219 | |
220 | if (m->msg_len > 4) |
221 | return -EINVAL; |
222 | |
223 | mutex_lock(&dst->buf_mutex); |
224 | |
225 | cmd = dst->buf; |
226 | cmd[1] = SET_DISEQC_CMD; |
227 | cmd[2] = m->msg_len; |
228 | memcpy(&cmd[3], m->msg, m->msg_len); |
229 | cmd[7] = vp702x_chksum(buf: cmd, f: 0, count: 7); |
230 | |
231 | vp702x_usb_inout_op(d: st->d, o: cmd, olen: 8, i: cmd, ilen: 10, msec: 100); |
232 | |
233 | if (cmd[2] == 0 && cmd[3] == 0) |
234 | deb_fe("diseqc cmd failed.\n" ); |
235 | else |
236 | deb_fe("diseqc cmd succeeded.\n" ); |
237 | |
238 | mutex_unlock(lock: &dst->buf_mutex); |
239 | |
240 | return 0; |
241 | } |
242 | |
243 | static int vp702x_fe_send_diseqc_burst(struct dvb_frontend *fe, |
244 | enum fe_sec_mini_cmd burst) |
245 | { |
246 | deb_fe("%s\n" ,__func__); |
247 | return 0; |
248 | } |
249 | |
250 | static int vp702x_fe_set_tone(struct dvb_frontend *fe, |
251 | enum fe_sec_tone_mode tone) |
252 | { |
253 | struct vp702x_fe_state *st = fe->demodulator_priv; |
254 | struct vp702x_device_state *dst = st->d->priv; |
255 | u8 *buf; |
256 | |
257 | deb_fe("%s\n" ,__func__); |
258 | |
259 | st->tone_mode = tone; |
260 | |
261 | if (tone == SEC_TONE_ON) |
262 | st->lnb_buf[2] = 0x02; |
263 | else |
264 | st->lnb_buf[2] = 0x00; |
265 | |
266 | st->lnb_buf[7] = vp702x_chksum(buf: st->lnb_buf, f: 0, count: 7); |
267 | |
268 | mutex_lock(&dst->buf_mutex); |
269 | |
270 | buf = dst->buf; |
271 | memcpy(buf, st->lnb_buf, 8); |
272 | |
273 | vp702x_usb_inout_op(d: st->d, o: buf, olen: 8, i: buf, ilen: 10, msec: 100); |
274 | if (buf[2] == 0 && buf[3] == 0) |
275 | deb_fe("set_tone cmd failed.\n" ); |
276 | else |
277 | deb_fe("set_tone cmd succeeded.\n" ); |
278 | |
279 | mutex_unlock(lock: &dst->buf_mutex); |
280 | |
281 | return 0; |
282 | } |
283 | |
284 | static int vp702x_fe_set_voltage(struct dvb_frontend *fe, |
285 | enum fe_sec_voltage voltage) |
286 | { |
287 | struct vp702x_fe_state *st = fe->demodulator_priv; |
288 | struct vp702x_device_state *dst = st->d->priv; |
289 | u8 *buf; |
290 | deb_fe("%s\n" ,__func__); |
291 | |
292 | st->voltage = voltage; |
293 | |
294 | if (voltage != SEC_VOLTAGE_OFF) |
295 | st->lnb_buf[4] = 0x01; |
296 | else |
297 | st->lnb_buf[4] = 0x00; |
298 | |
299 | st->lnb_buf[7] = vp702x_chksum(buf: st->lnb_buf, f: 0, count: 7); |
300 | |
301 | mutex_lock(&dst->buf_mutex); |
302 | |
303 | buf = dst->buf; |
304 | memcpy(buf, st->lnb_buf, 8); |
305 | |
306 | vp702x_usb_inout_op(d: st->d, o: buf, olen: 8, i: buf, ilen: 10, msec: 100); |
307 | if (buf[2] == 0 && buf[3] == 0) |
308 | deb_fe("set_voltage cmd failed.\n" ); |
309 | else |
310 | deb_fe("set_voltage cmd succeeded.\n" ); |
311 | |
312 | mutex_unlock(lock: &dst->buf_mutex); |
313 | return 0; |
314 | } |
315 | |
316 | static void vp702x_fe_release(struct dvb_frontend* fe) |
317 | { |
318 | struct vp702x_fe_state *st = fe->demodulator_priv; |
319 | kfree(objp: st); |
320 | } |
321 | |
322 | static const struct dvb_frontend_ops vp702x_fe_ops; |
323 | |
324 | struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d) |
325 | { |
326 | struct vp702x_fe_state *s = kzalloc(size: sizeof(struct vp702x_fe_state), GFP_KERNEL); |
327 | if (s == NULL) |
328 | goto error; |
329 | |
330 | s->d = d; |
331 | |
332 | memcpy(&s->fe.ops,&vp702x_fe_ops,sizeof(struct dvb_frontend_ops)); |
333 | s->fe.demodulator_priv = s; |
334 | |
335 | s->lnb_buf[1] = SET_LNB_POWER; |
336 | s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */ |
337 | |
338 | return &s->fe; |
339 | error: |
340 | return NULL; |
341 | } |
342 | |
343 | |
344 | static const struct dvb_frontend_ops vp702x_fe_ops = { |
345 | .delsys = { SYS_DVBS }, |
346 | .info = { |
347 | .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S" , |
348 | .frequency_min_hz = 950 * MHz, |
349 | .frequency_max_hz = 2150 * MHz, |
350 | .frequency_stepsize_hz = 1 * MHz, |
351 | .symbol_rate_min = 1000000, |
352 | .symbol_rate_max = 45000000, |
353 | .symbol_rate_tolerance = 500, /* ppm */ |
354 | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
355 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | |
356 | FE_CAN_QPSK | |
357 | FE_CAN_FEC_AUTO |
358 | }, |
359 | .release = vp702x_fe_release, |
360 | |
361 | .init = vp702x_fe_init, |
362 | .sleep = vp702x_fe_sleep, |
363 | |
364 | .set_frontend = vp702x_fe_set_frontend, |
365 | .get_tune_settings = vp702x_fe_get_tune_settings, |
366 | |
367 | .read_status = vp702x_fe_read_status, |
368 | .read_ber = vp702x_fe_read_ber, |
369 | .read_signal_strength = vp702x_fe_read_signal_strength, |
370 | .read_snr = vp702x_fe_read_snr, |
371 | .read_ucblocks = vp702x_fe_read_unc_blocks, |
372 | |
373 | .diseqc_send_master_cmd = vp702x_fe_send_diseqc_msg, |
374 | .diseqc_send_burst = vp702x_fe_send_diseqc_burst, |
375 | .set_tone = vp702x_fe_set_tone, |
376 | .set_voltage = vp702x_fe_set_voltage, |
377 | }; |
378 | |