1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ddbridge-sx8.c: Digital Devices MAX SX8 driver |
4 | * |
5 | * Copyright (C) 2018 Digital Devices GmbH |
6 | * Marcus Metzler <mocm@metzlerbros.de> |
7 | * Ralph Metzler <rjkm@metzlerbros.de> |
8 | */ |
9 | |
10 | #include "ddbridge.h" |
11 | #include "ddbridge-io.h" |
12 | #include "ddbridge-mci.h" |
13 | |
14 | static const u32 MCLK = (1550000000 / 12); |
15 | static const u32 MAX_LDPC_BITRATE = (720000000); |
16 | static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); |
17 | |
18 | #define SX8_TUNER_NUM 4 |
19 | #define SX8_DEMOD_NUM 8 |
20 | #define SX8_DEMOD_NONE 0xff |
21 | |
22 | struct sx8_base { |
23 | struct mci_base mci_base; |
24 | |
25 | u8 tuner_use_count[SX8_TUNER_NUM]; |
26 | u32 gain_mode[SX8_TUNER_NUM]; |
27 | |
28 | u32 used_ldpc_bitrate[SX8_DEMOD_NUM]; |
29 | u8 demod_in_use[SX8_DEMOD_NUM]; |
30 | u32 iq_mode; |
31 | u32 burst_size; |
32 | u32 direct_mode; |
33 | }; |
34 | |
35 | struct sx8 { |
36 | struct mci mci; |
37 | |
38 | int first_time_lock; |
39 | int started; |
40 | struct mci_result signal_info; |
41 | |
42 | u32 bb_mode; |
43 | u32 local_frequency; |
44 | }; |
45 | |
46 | static void release(struct dvb_frontend *fe) |
47 | { |
48 | struct sx8 *state = fe->demodulator_priv; |
49 | struct mci_base *mci_base = state->mci.base; |
50 | |
51 | mci_base->count--; |
52 | if (mci_base->count == 0) { |
53 | list_del(entry: &mci_base->mci_list); |
54 | kfree(objp: mci_base); |
55 | } |
56 | kfree(objp: state); |
57 | } |
58 | |
59 | static int get_info(struct dvb_frontend *fe) |
60 | { |
61 | int stat; |
62 | struct sx8 *state = fe->demodulator_priv; |
63 | struct mci_command cmd; |
64 | |
65 | memset(&cmd, 0, sizeof(cmd)); |
66 | cmd.command = MCI_CMD_GETSIGNALINFO; |
67 | cmd.demod = state->mci.demod; |
68 | stat = ddb_mci_cmd(state: &state->mci, command: &cmd, result: &state->signal_info); |
69 | return stat; |
70 | } |
71 | |
72 | static int get_snr(struct dvb_frontend *fe) |
73 | { |
74 | struct sx8 *state = fe->demodulator_priv; |
75 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
76 | |
77 | p->cnr.len = 1; |
78 | p->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
79 | p->cnr.stat[0].svalue = |
80 | (s64)state->signal_info.dvbs2_signal_info.signal_to_noise |
81 | * 10; |
82 | return 0; |
83 | } |
84 | |
85 | static int get_strength(struct dvb_frontend *fe) |
86 | { |
87 | struct sx8 *state = fe->demodulator_priv; |
88 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
89 | s32 str; |
90 | |
91 | str = 100000 - |
92 | (state->signal_info.dvbs2_signal_info.channel_power |
93 | * 10 + 108750); |
94 | p->strength.len = 1; |
95 | p->strength.stat[0].scale = FE_SCALE_DECIBEL; |
96 | p->strength.stat[0].svalue = str; |
97 | return 0; |
98 | } |
99 | |
100 | static int read_status(struct dvb_frontend *fe, enum fe_status *status) |
101 | { |
102 | int stat; |
103 | struct sx8 *state = fe->demodulator_priv; |
104 | struct mci_command cmd; |
105 | struct mci_result res; |
106 | |
107 | cmd.command = MCI_CMD_GETSTATUS; |
108 | cmd.demod = state->mci.demod; |
109 | stat = ddb_mci_cmd(state: &state->mci, command: &cmd, result: &res); |
110 | if (stat) |
111 | return stat; |
112 | *status = 0x00; |
113 | get_info(fe); |
114 | get_strength(fe); |
115 | if (res.status == SX8_DEMOD_WAIT_MATYPE) |
116 | *status = 0x0f; |
117 | if (res.status == SX8_DEMOD_LOCKED) { |
118 | *status = 0x1f; |
119 | get_snr(fe); |
120 | } |
121 | return stat; |
122 | } |
123 | |
124 | static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) |
125 | { |
126 | struct sx8 *state = fe->demodulator_priv; |
127 | struct mci_base *mci_base = state->mci.base; |
128 | struct sx8_base *sx8_base = (struct sx8_base *)mci_base; |
129 | struct mci_command cmd; |
130 | |
131 | memset(&cmd, 0, sizeof(cmd)); |
132 | cmd.tuner = state->mci.tuner; |
133 | cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; |
134 | cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner]; |
135 | return ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
136 | } |
137 | |
138 | static int stop(struct dvb_frontend *fe) |
139 | { |
140 | struct sx8 *state = fe->demodulator_priv; |
141 | struct mci_base *mci_base = state->mci.base; |
142 | struct sx8_base *sx8_base = (struct sx8_base *)mci_base; |
143 | struct mci_command cmd; |
144 | u32 input = state->mci.tuner; |
145 | |
146 | memset(&cmd, 0, sizeof(cmd)); |
147 | if (state->mci.demod != SX8_DEMOD_NONE) { |
148 | cmd.command = MCI_CMD_STOP; |
149 | cmd.demod = state->mci.demod; |
150 | ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
151 | if (sx8_base->iq_mode) { |
152 | cmd.command = SX8_CMD_DISABLE_IQOUTPUT; |
153 | cmd.demod = state->mci.demod; |
154 | cmd.output = 0; |
155 | ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
156 | ddb_mci_config(state: &state->mci, SX8_TSCONFIG_MODE_NORMAL); |
157 | } |
158 | } |
159 | mutex_lock(&mci_base->tuner_lock); |
160 | sx8_base->tuner_use_count[input]--; |
161 | if (!sx8_base->tuner_use_count[input]) |
162 | mci_set_tuner(fe, tuner: input, on: 0); |
163 | if (state->mci.demod < SX8_DEMOD_NUM) { |
164 | sx8_base->demod_in_use[state->mci.demod] = 0; |
165 | state->mci.demod = SX8_DEMOD_NONE; |
166 | } |
167 | sx8_base->used_ldpc_bitrate[state->mci.nr] = 0; |
168 | sx8_base->iq_mode = 0; |
169 | mutex_unlock(lock: &mci_base->tuner_lock); |
170 | state->started = 0; |
171 | return 0; |
172 | } |
173 | |
174 | static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) |
175 | { |
176 | struct sx8 *state = fe->demodulator_priv; |
177 | struct mci_base *mci_base = state->mci.base; |
178 | struct sx8_base *sx8_base = (struct sx8_base *)mci_base; |
179 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
180 | u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; |
181 | u32 used_demods = 0; |
182 | struct mci_command cmd; |
183 | u32 input = state->mci.tuner; |
184 | u32 bits_per_symbol = 0; |
185 | int i = -1, stat = 0; |
186 | |
187 | if (p->symbol_rate >= (MCLK / 2)) |
188 | flags &= ~1; |
189 | if ((flags & 3) == 0) |
190 | return -EINVAL; |
191 | |
192 | if (flags & 2) { |
193 | u32 tmp = modmask; |
194 | |
195 | bits_per_symbol = 1; |
196 | while (tmp & 1) { |
197 | tmp >>= 1; |
198 | bits_per_symbol++; |
199 | } |
200 | } |
201 | |
202 | mutex_lock(&mci_base->tuner_lock); |
203 | if (sx8_base->iq_mode) { |
204 | stat = -EBUSY; |
205 | goto unlock; |
206 | } |
207 | |
208 | if (sx8_base->direct_mode) { |
209 | if (p->symbol_rate >= MCLK / 2) { |
210 | if (state->mci.nr < 4) |
211 | i = state->mci.nr; |
212 | } else { |
213 | i = state->mci.nr; |
214 | } |
215 | } else { |
216 | for (i = 0; i < SX8_DEMOD_NUM; i++) { |
217 | used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i]; |
218 | if (sx8_base->demod_in_use[i]) |
219 | used_demods++; |
220 | } |
221 | if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || |
222 | ((ts_config & SX8_TSCONFIG_MODE_MASK) > |
223 | SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { |
224 | stat = -EBUSY; |
225 | goto unlock; |
226 | } |
227 | free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; |
228 | if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) |
229 | free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; |
230 | |
231 | while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) |
232 | bits_per_symbol--; |
233 | if (bits_per_symbol < 2) { |
234 | stat = -EBUSY; |
235 | goto unlock; |
236 | } |
237 | |
238 | modmask &= ((1 << (bits_per_symbol - 1)) - 1); |
239 | if (((flags & 0x02) != 0) && modmask == 0) { |
240 | stat = -EBUSY; |
241 | goto unlock; |
242 | } |
243 | |
244 | i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; |
245 | while (i >= 0 && sx8_base->demod_in_use[i]) |
246 | i--; |
247 | } |
248 | |
249 | if (i < 0) { |
250 | stat = -EBUSY; |
251 | goto unlock; |
252 | } |
253 | sx8_base->demod_in_use[i] = 1; |
254 | sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate |
255 | * bits_per_symbol; |
256 | state->mci.demod = i; |
257 | |
258 | if (!sx8_base->tuner_use_count[input]) |
259 | mci_set_tuner(fe, tuner: input, on: 1); |
260 | sx8_base->tuner_use_count[input]++; |
261 | sx8_base->iq_mode = (ts_config > 1); |
262 | unlock: |
263 | mutex_unlock(lock: &mci_base->tuner_lock); |
264 | if (stat) |
265 | return stat; |
266 | memset(&cmd, 0, sizeof(cmd)); |
267 | |
268 | if (sx8_base->iq_mode) { |
269 | cmd.command = SX8_CMD_ENABLE_IQOUTPUT; |
270 | cmd.demod = state->mci.demod; |
271 | cmd.output = 0; |
272 | ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
273 | ddb_mci_config(state: &state->mci, config: ts_config); |
274 | } |
275 | if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) |
276 | flags |= 0x80; |
277 | dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n" , |
278 | state->mci.nr, state->mci.tuner, state->mci.demod); |
279 | cmd.command = MCI_CMD_SEARCH_DVBS; |
280 | cmd.dvbs2_search.flags = flags; |
281 | cmd.dvbs2_search.s2_modulation_mask = modmask; |
282 | cmd.dvbs2_search.retry = 2; |
283 | cmd.dvbs2_search.frequency = p->frequency * 1000; |
284 | cmd.dvbs2_search.symbol_rate = p->symbol_rate; |
285 | cmd.dvbs2_search.scrambling_sequence_index = |
286 | p->scrambling_sequence_index | 0x80000000; |
287 | cmd.dvbs2_search.input_stream_id = |
288 | (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; |
289 | cmd.tuner = state->mci.tuner; |
290 | cmd.demod = state->mci.demod; |
291 | cmd.output = state->mci.nr; |
292 | if (p->stream_id == 0x80000000) |
293 | cmd.output |= 0x80; |
294 | stat = ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
295 | if (stat) |
296 | stop(fe); |
297 | return stat; |
298 | } |
299 | |
300 | static int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off, |
301 | u32 ts_config) |
302 | { |
303 | struct sx8 *state = fe->demodulator_priv; |
304 | struct mci_base *mci_base = state->mci.base; |
305 | struct sx8_base *sx8_base = (struct sx8_base *)mci_base; |
306 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
307 | u32 used_demods = 0; |
308 | struct mci_command cmd; |
309 | u32 input = state->mci.tuner; |
310 | int i, stat = 0; |
311 | |
312 | mutex_lock(&mci_base->tuner_lock); |
313 | if (sx8_base->iq_mode) { |
314 | stat = -EBUSY; |
315 | goto unlock; |
316 | } |
317 | for (i = 0; i < SX8_DEMOD_NUM; i++) |
318 | if (sx8_base->demod_in_use[i]) |
319 | used_demods++; |
320 | if (used_demods > 0) { |
321 | stat = -EBUSY; |
322 | goto unlock; |
323 | } |
324 | state->mci.demod = 0; |
325 | if (!sx8_base->tuner_use_count[input]) |
326 | mci_set_tuner(fe, tuner: input, on: 1); |
327 | sx8_base->tuner_use_count[input]++; |
328 | sx8_base->iq_mode = (ts_config > 1); |
329 | unlock: |
330 | mutex_unlock(lock: &mci_base->tuner_lock); |
331 | if (stat) |
332 | return stat; |
333 | |
334 | memset(&cmd, 0, sizeof(cmd)); |
335 | cmd.command = SX8_CMD_START_IQ; |
336 | cmd.sx8_start_iq.flags = flags; |
337 | cmd.sx8_start_iq.roll_off = roll_off; |
338 | cmd.sx8_start_iq.frequency = p->frequency * 1000; |
339 | cmd.sx8_start_iq.symbol_rate = p->symbol_rate; |
340 | cmd.tuner = state->mci.tuner; |
341 | cmd.demod = state->mci.demod; |
342 | stat = ddb_mci_cmd(state: &state->mci, command: &cmd, NULL); |
343 | if (stat) |
344 | stop(fe); |
345 | ddb_mci_config(state: &state->mci, config: ts_config); |
346 | return stat; |
347 | } |
348 | |
349 | static int set_parameters(struct dvb_frontend *fe) |
350 | { |
351 | int stat = 0; |
352 | struct sx8 *state = fe->demodulator_priv; |
353 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
354 | u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi; |
355 | |
356 | if (state->started) |
357 | stop(fe); |
358 | |
359 | isi = p->stream_id; |
360 | if (isi != NO_STREAM_ID_FILTER) |
361 | iq_mode = (isi & 0x30000000) >> 28; |
362 | |
363 | if (iq_mode) |
364 | ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); |
365 | if (iq_mode < 3) { |
366 | u32 mask; |
367 | |
368 | switch (p->modulation) { |
369 | /* uncomment whenever these modulations hit the DVB API |
370 | * case APSK_256: |
371 | * mask = 0x7f; |
372 | * break; |
373 | * case APSK_128: |
374 | * mask = 0x3f; |
375 | * break; |
376 | * case APSK_64: |
377 | * mask = 0x1f; |
378 | * break; |
379 | */ |
380 | case APSK_32: |
381 | mask = 0x0f; |
382 | break; |
383 | case APSK_16: |
384 | mask = 0x07; |
385 | break; |
386 | default: |
387 | mask = 0x03; |
388 | break; |
389 | } |
390 | stat = start(fe, flags: 3, modmask: mask, ts_config); |
391 | } else { |
392 | stat = start_iq(fe, flags: 0, roll_off: 4, ts_config); |
393 | } |
394 | if (!stat) { |
395 | state->started = 1; |
396 | state->first_time_lock = 1; |
397 | state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; |
398 | } |
399 | |
400 | return stat; |
401 | } |
402 | |
403 | static int tune(struct dvb_frontend *fe, bool re_tune, |
404 | unsigned int mode_flags, |
405 | unsigned int *delay, enum fe_status *status) |
406 | { |
407 | int r; |
408 | |
409 | if (re_tune) { |
410 | r = set_parameters(fe); |
411 | if (r) |
412 | return r; |
413 | } |
414 | r = read_status(fe, status); |
415 | if (r) |
416 | return r; |
417 | |
418 | if (*status & FE_HAS_LOCK) |
419 | return 0; |
420 | *delay = HZ / 10; |
421 | return 0; |
422 | } |
423 | |
424 | static enum dvbfe_algo get_algo(struct dvb_frontend *fe) |
425 | { |
426 | return DVBFE_ALGO_HW; |
427 | } |
428 | |
429 | static int set_input(struct dvb_frontend *fe, int input) |
430 | { |
431 | struct sx8 *state = fe->demodulator_priv; |
432 | struct mci_base *mci_base = state->mci.base; |
433 | |
434 | if (input >= SX8_TUNER_NUM) |
435 | return -EINVAL; |
436 | |
437 | state->mci.tuner = input; |
438 | dev_dbg(mci_base->dev, "MCI-%d: input=%d\n" , state->mci.nr, input); |
439 | return 0; |
440 | } |
441 | |
442 | static struct dvb_frontend_ops sx8_ops = { |
443 | .delsys = { SYS_DVBS, SYS_DVBS2 }, |
444 | .info = { |
445 | .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X" , |
446 | .frequency_min_hz = 950 * MHz, |
447 | .frequency_max_hz = 2150 * MHz, |
448 | .symbol_rate_min = 100000, |
449 | .symbol_rate_max = 100000000, |
450 | .caps = FE_CAN_INVERSION_AUTO | |
451 | FE_CAN_FEC_AUTO | |
452 | FE_CAN_QPSK | |
453 | FE_CAN_2G_MODULATION | |
454 | FE_CAN_MULTISTREAM, |
455 | }, |
456 | .get_frontend_algo = get_algo, |
457 | .tune = tune, |
458 | .release = release, |
459 | .read_status = read_status, |
460 | }; |
461 | |
462 | static int init(struct mci *mci) |
463 | { |
464 | struct sx8 *state = (struct sx8 *)mci; |
465 | |
466 | state->mci.demod = SX8_DEMOD_NONE; |
467 | return 0; |
468 | } |
469 | |
470 | const struct mci_cfg ddb_max_sx8_cfg = { |
471 | .type = 0, |
472 | .fe_ops = &sx8_ops, |
473 | .base_size = sizeof(struct sx8_base), |
474 | .state_size = sizeof(struct sx8), |
475 | .init = init, |
476 | .set_input = set_input, |
477 | }; |
478 | |