1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | NxtWave Communications - NXT6000 demodulator driver |
4 | |
5 | Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org> |
6 | Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au> |
7 | |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/init.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/string.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #include <media/dvb_frontend.h> |
19 | #include "nxt6000_priv.h" |
20 | #include "nxt6000.h" |
21 | |
22 | |
23 | |
24 | struct nxt6000_state { |
25 | struct i2c_adapter* i2c; |
26 | /* configuration settings */ |
27 | const struct nxt6000_config* config; |
28 | struct dvb_frontend frontend; |
29 | }; |
30 | |
31 | static int debug; |
32 | #define dprintk(fmt, arg...) do { \ |
33 | if (debug) \ |
34 | printk(KERN_DEBUG pr_fmt("%s: " fmt), \ |
35 | __func__, ##arg); \ |
36 | } while (0) |
37 | |
38 | static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) |
39 | { |
40 | u8 buf[] = { reg, data }; |
41 | struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; |
42 | int ret; |
43 | |
44 | if ((ret = i2c_transfer(adap: state->i2c, msgs: &msg, num: 1)) != 1) |
45 | dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n" , reg, data, ret); |
46 | |
47 | return (ret != 1) ? -EIO : 0; |
48 | } |
49 | |
50 | static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg) |
51 | { |
52 | int ret; |
53 | u8 b0[] = { reg }; |
54 | u8 b1[] = { 0 }; |
55 | struct i2c_msg msgs[] = { |
56 | {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, |
57 | {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} |
58 | }; |
59 | |
60 | ret = i2c_transfer(adap: state->i2c, msgs, num: 2); |
61 | |
62 | if (ret != 2) |
63 | dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n" , reg, ret); |
64 | |
65 | return b1[0]; |
66 | } |
67 | |
68 | static void nxt6000_reset(struct nxt6000_state* state) |
69 | { |
70 | u8 val; |
71 | |
72 | val = nxt6000_readreg(state, OFDM_COR_CTL); |
73 | |
74 | nxt6000_writereg(state, OFDM_COR_CTL, data: val & ~COREACT); |
75 | nxt6000_writereg(state, OFDM_COR_CTL, data: val | COREACT); |
76 | } |
77 | |
78 | static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth) |
79 | { |
80 | u16 nominal_rate; |
81 | int result; |
82 | |
83 | switch (bandwidth) { |
84 | case 6000000: |
85 | nominal_rate = 0x55B7; |
86 | break; |
87 | |
88 | case 7000000: |
89 | nominal_rate = 0x6400; |
90 | break; |
91 | |
92 | case 8000000: |
93 | nominal_rate = 0x7249; |
94 | break; |
95 | |
96 | default: |
97 | return -EINVAL; |
98 | } |
99 | |
100 | if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, data: nominal_rate & 0xFF)) < 0) |
101 | return result; |
102 | |
103 | return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, data: (nominal_rate >> 8) & 0xFF); |
104 | } |
105 | |
106 | static int nxt6000_set_guard_interval(struct nxt6000_state *state, |
107 | enum fe_guard_interval guard_interval) |
108 | { |
109 | switch (guard_interval) { |
110 | |
111 | case GUARD_INTERVAL_1_32: |
112 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); |
113 | |
114 | case GUARD_INTERVAL_1_16: |
115 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); |
116 | |
117 | case GUARD_INTERVAL_AUTO: |
118 | case GUARD_INTERVAL_1_8: |
119 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); |
120 | |
121 | case GUARD_INTERVAL_1_4: |
122 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); |
123 | |
124 | default: |
125 | return -EINVAL; |
126 | } |
127 | } |
128 | |
129 | static int nxt6000_set_inversion(struct nxt6000_state *state, |
130 | enum fe_spectral_inversion inversion) |
131 | { |
132 | switch (inversion) { |
133 | |
134 | case INVERSION_OFF: |
135 | return nxt6000_writereg(state, OFDM_ITB_CTL, data: 0x00); |
136 | |
137 | case INVERSION_ON: |
138 | return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); |
139 | |
140 | default: |
141 | return -EINVAL; |
142 | |
143 | } |
144 | } |
145 | |
146 | static int |
147 | nxt6000_set_transmission_mode(struct nxt6000_state *state, |
148 | enum fe_transmit_mode transmission_mode) |
149 | { |
150 | int result; |
151 | |
152 | switch (transmission_mode) { |
153 | |
154 | case TRANSMISSION_MODE_2K: |
155 | if ((result = nxt6000_writereg(state, EN_DMD_RACQ, data: 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) |
156 | return result; |
157 | |
158 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); |
159 | |
160 | case TRANSMISSION_MODE_8K: |
161 | case TRANSMISSION_MODE_AUTO: |
162 | if ((result = nxt6000_writereg(state, EN_DMD_RACQ, data: 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) |
163 | return result; |
164 | |
165 | return nxt6000_writereg(state, OFDM_COR_MODEGUARD, data: (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); |
166 | |
167 | default: |
168 | return -EINVAL; |
169 | |
170 | } |
171 | } |
172 | |
173 | static void nxt6000_setup(struct dvb_frontend* fe) |
174 | { |
175 | struct nxt6000_state* state = fe->demodulator_priv; |
176 | |
177 | nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); |
178 | nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ data: (0x01 << 1) | 0x01); |
179 | nxt6000_writereg(state, VIT_BERTIME_2, data: 0x00); // BER Timer = 0x000200 * 256 = 131072 bits |
180 | nxt6000_writereg(state, VIT_BERTIME_1, data: 0x02); // |
181 | nxt6000_writereg(state, VIT_BERTIME_0, data: 0x00); // |
182 | nxt6000_writereg(state, VIT_COR_INTEN, data: 0x98); // Enable BER interrupts |
183 | nxt6000_writereg(state, VIT_COR_CTL, data: 0x82); // Enable BER measurement |
184 | nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC | 0x02 ); |
185 | nxt6000_writereg(state, OFDM_COR_CTL, data: (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); |
186 | nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); |
187 | nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); |
188 | nxt6000_writereg(state, OFDM_ITB_FREQ_1, data: 0x06); |
189 | nxt6000_writereg(state, OFDM_ITB_FREQ_2, data: 0x31); |
190 | nxt6000_writereg(state, OFDM_CAS_CTL, data: (0x01 << 7) | (0x02 << 3) | 0x04); |
191 | nxt6000_writereg(state, CAS_FREQ, data: 0xBB); /* CHECKME */ |
192 | nxt6000_writereg(state, OFDM_SYR_CTL, data: 1 << 2); |
193 | nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); |
194 | nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, data: 0x49); |
195 | nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, data: 0x72); |
196 | nxt6000_writereg(state, ANALOG_CONTROL_0, data: 1 << 5); |
197 | nxt6000_writereg(state, EN_DMD_RACQ, data: (1 << 7) | (3 << 4) | 2); |
198 | nxt6000_writereg(state, DIAG_CONFIG, TB_SET); |
199 | |
200 | if (state->config->clock_inversion) |
201 | nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); |
202 | else |
203 | nxt6000_writereg(state, SUB_DIAG_MODE_SEL, data: 0); |
204 | |
205 | nxt6000_writereg(state, TS_FORMAT, data: 0); |
206 | } |
207 | |
208 | static void nxt6000_dump_status(struct nxt6000_state *state) |
209 | { |
210 | u8 val; |
211 | |
212 | #if 0 |
213 | pr_info("RS_COR_STAT: 0x%02X\n" , |
214 | nxt6000_readreg(fe, RS_COR_STAT)); |
215 | pr_info("VIT_SYNC_STATUS: 0x%02X\n" , |
216 | nxt6000_readreg(fe, VIT_SYNC_STATUS)); |
217 | pr_info("OFDM_COR_STAT: 0x%02X\n" , |
218 | nxt6000_readreg(fe, OFDM_COR_STAT)); |
219 | pr_info("OFDM_SYR_STAT: 0x%02X\n" , |
220 | nxt6000_readreg(fe, OFDM_SYR_STAT)); |
221 | pr_info("OFDM_TPS_RCVD_1: 0x%02X\n" , |
222 | nxt6000_readreg(fe, OFDM_TPS_RCVD_1)); |
223 | pr_info("OFDM_TPS_RCVD_2: 0x%02X\n" , |
224 | nxt6000_readreg(fe, OFDM_TPS_RCVD_2)); |
225 | pr_info("OFDM_TPS_RCVD_3: 0x%02X\n" , |
226 | nxt6000_readreg(fe, OFDM_TPS_RCVD_3)); |
227 | pr_info("OFDM_TPS_RCVD_4: 0x%02X\n" , |
228 | nxt6000_readreg(fe, OFDM_TPS_RCVD_4)); |
229 | pr_info("OFDM_TPS_RESERVED_1: 0x%02X\n" , |
230 | nxt6000_readreg(fe, OFDM_TPS_RESERVED_1)); |
231 | pr_info("OFDM_TPS_RESERVED_2: 0x%02X\n" , |
232 | nxt6000_readreg(fe, OFDM_TPS_RESERVED_2)); |
233 | #endif |
234 | pr_info("NXT6000 status:" ); |
235 | |
236 | val = nxt6000_readreg(state, RS_COR_STAT); |
237 | |
238 | pr_cont(" DATA DESCR LOCK: %d," , val & 0x01); |
239 | pr_cont(" DATA SYNC LOCK: %d," , (val >> 1) & 0x01); |
240 | |
241 | val = nxt6000_readreg(state, VIT_SYNC_STATUS); |
242 | |
243 | pr_cont(" VITERBI LOCK: %d," , (val >> 7) & 0x01); |
244 | |
245 | switch ((val >> 4) & 0x07) { |
246 | |
247 | case 0x00: |
248 | pr_cont(" VITERBI CODERATE: 1/2," ); |
249 | break; |
250 | |
251 | case 0x01: |
252 | pr_cont(" VITERBI CODERATE: 2/3," ); |
253 | break; |
254 | |
255 | case 0x02: |
256 | pr_cont(" VITERBI CODERATE: 3/4," ); |
257 | break; |
258 | |
259 | case 0x03: |
260 | pr_cont(" VITERBI CODERATE: 5/6," ); |
261 | break; |
262 | |
263 | case 0x04: |
264 | pr_cont(" VITERBI CODERATE: 7/8," ); |
265 | break; |
266 | |
267 | default: |
268 | pr_cont(" VITERBI CODERATE: Reserved," ); |
269 | |
270 | } |
271 | |
272 | val = nxt6000_readreg(state, OFDM_COR_STAT); |
273 | |
274 | pr_cont(" CHCTrack: %d," , (val >> 7) & 0x01); |
275 | pr_cont(" TPSLock: %d," , (val >> 6) & 0x01); |
276 | pr_cont(" SYRLock: %d," , (val >> 5) & 0x01); |
277 | pr_cont(" AGCLock: %d," , (val >> 4) & 0x01); |
278 | |
279 | switch (val & 0x0F) { |
280 | |
281 | case 0x00: |
282 | pr_cont(" CoreState: IDLE," ); |
283 | break; |
284 | |
285 | case 0x02: |
286 | pr_cont(" CoreState: WAIT_AGC," ); |
287 | break; |
288 | |
289 | case 0x03: |
290 | pr_cont(" CoreState: WAIT_SYR," ); |
291 | break; |
292 | |
293 | case 0x04: |
294 | pr_cont(" CoreState: WAIT_PPM," ); |
295 | break; |
296 | |
297 | case 0x01: |
298 | pr_cont(" CoreState: WAIT_TRL," ); |
299 | break; |
300 | |
301 | case 0x05: |
302 | pr_cont(" CoreState: WAIT_TPS," ); |
303 | break; |
304 | |
305 | case 0x06: |
306 | pr_cont(" CoreState: MONITOR_TPS," ); |
307 | break; |
308 | |
309 | default: |
310 | pr_cont(" CoreState: Reserved," ); |
311 | |
312 | } |
313 | |
314 | val = nxt6000_readreg(state, OFDM_SYR_STAT); |
315 | |
316 | pr_cont(" SYRLock: %d," , (val >> 4) & 0x01); |
317 | pr_cont(" SYRMode: %s," , (val >> 2) & 0x01 ? "8K" : "2K" ); |
318 | |
319 | switch ((val >> 4) & 0x03) { |
320 | |
321 | case 0x00: |
322 | pr_cont(" SYRGuard: 1/32," ); |
323 | break; |
324 | |
325 | case 0x01: |
326 | pr_cont(" SYRGuard: 1/16," ); |
327 | break; |
328 | |
329 | case 0x02: |
330 | pr_cont(" SYRGuard: 1/8," ); |
331 | break; |
332 | |
333 | case 0x03: |
334 | pr_cont(" SYRGuard: 1/4," ); |
335 | break; |
336 | } |
337 | |
338 | val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); |
339 | |
340 | switch ((val >> 4) & 0x07) { |
341 | |
342 | case 0x00: |
343 | pr_cont(" TPSLP: 1/2," ); |
344 | break; |
345 | |
346 | case 0x01: |
347 | pr_cont(" TPSLP: 2/3," ); |
348 | break; |
349 | |
350 | case 0x02: |
351 | pr_cont(" TPSLP: 3/4," ); |
352 | break; |
353 | |
354 | case 0x03: |
355 | pr_cont(" TPSLP: 5/6," ); |
356 | break; |
357 | |
358 | case 0x04: |
359 | pr_cont(" TPSLP: 7/8," ); |
360 | break; |
361 | |
362 | default: |
363 | pr_cont(" TPSLP: Reserved," ); |
364 | |
365 | } |
366 | |
367 | switch (val & 0x07) { |
368 | |
369 | case 0x00: |
370 | pr_cont(" TPSHP: 1/2," ); |
371 | break; |
372 | |
373 | case 0x01: |
374 | pr_cont(" TPSHP: 2/3," ); |
375 | break; |
376 | |
377 | case 0x02: |
378 | pr_cont(" TPSHP: 3/4," ); |
379 | break; |
380 | |
381 | case 0x03: |
382 | pr_cont(" TPSHP: 5/6," ); |
383 | break; |
384 | |
385 | case 0x04: |
386 | pr_cont(" TPSHP: 7/8," ); |
387 | break; |
388 | |
389 | default: |
390 | pr_cont(" TPSHP: Reserved," ); |
391 | |
392 | } |
393 | |
394 | val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); |
395 | |
396 | pr_cont(" TPSMode: %s," , val & 0x01 ? "8K" : "2K" ); |
397 | |
398 | switch ((val >> 4) & 0x03) { |
399 | |
400 | case 0x00: |
401 | pr_cont(" TPSGuard: 1/32," ); |
402 | break; |
403 | |
404 | case 0x01: |
405 | pr_cont(" TPSGuard: 1/16," ); |
406 | break; |
407 | |
408 | case 0x02: |
409 | pr_cont(" TPSGuard: 1/8," ); |
410 | break; |
411 | |
412 | case 0x03: |
413 | pr_cont(" TPSGuard: 1/4," ); |
414 | break; |
415 | |
416 | } |
417 | |
418 | /* Strange magic required to gain access to RF_AGC_STATUS */ |
419 | nxt6000_readreg(state, RF_AGC_VAL_1); |
420 | val = nxt6000_readreg(state, RF_AGC_STATUS); |
421 | val = nxt6000_readreg(state, RF_AGC_STATUS); |
422 | |
423 | pr_cont(" RF AGC LOCK: %d," , (val >> 4) & 0x01); |
424 | pr_cont("\n" ); |
425 | } |
426 | |
427 | static int nxt6000_read_status(struct dvb_frontend *fe, enum fe_status *status) |
428 | { |
429 | u8 core_status; |
430 | struct nxt6000_state* state = fe->demodulator_priv; |
431 | |
432 | *status = 0; |
433 | |
434 | core_status = nxt6000_readreg(state, OFDM_COR_STAT); |
435 | |
436 | if (core_status & AGCLOCKED) |
437 | *status |= FE_HAS_SIGNAL; |
438 | |
439 | if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) |
440 | *status |= FE_HAS_CARRIER; |
441 | |
442 | if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) |
443 | *status |= FE_HAS_VITERBI; |
444 | |
445 | if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS) |
446 | *status |= FE_HAS_SYNC; |
447 | |
448 | if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))) |
449 | *status |= FE_HAS_LOCK; |
450 | |
451 | if (debug) |
452 | nxt6000_dump_status(state); |
453 | |
454 | return 0; |
455 | } |
456 | |
457 | static int nxt6000_init(struct dvb_frontend* fe) |
458 | { |
459 | struct nxt6000_state* state = fe->demodulator_priv; |
460 | |
461 | nxt6000_reset(state); |
462 | nxt6000_setup(fe); |
463 | |
464 | return 0; |
465 | } |
466 | |
467 | static int nxt6000_set_frontend(struct dvb_frontend *fe) |
468 | { |
469 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
470 | struct nxt6000_state* state = fe->demodulator_priv; |
471 | int result; |
472 | |
473 | if (fe->ops.tuner_ops.set_params) { |
474 | fe->ops.tuner_ops.set_params(fe); |
475 | if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); |
476 | } |
477 | |
478 | result = nxt6000_set_bandwidth(state, bandwidth: p->bandwidth_hz); |
479 | if (result < 0) |
480 | return result; |
481 | |
482 | result = nxt6000_set_guard_interval(state, guard_interval: p->guard_interval); |
483 | if (result < 0) |
484 | return result; |
485 | |
486 | result = nxt6000_set_transmission_mode(state, transmission_mode: p->transmission_mode); |
487 | if (result < 0) |
488 | return result; |
489 | |
490 | result = nxt6000_set_inversion(state, inversion: p->inversion); |
491 | if (result < 0) |
492 | return result; |
493 | |
494 | msleep(msecs: 500); |
495 | return 0; |
496 | } |
497 | |
498 | static void nxt6000_release(struct dvb_frontend* fe) |
499 | { |
500 | struct nxt6000_state* state = fe->demodulator_priv; |
501 | kfree(objp: state); |
502 | } |
503 | |
504 | static int nxt6000_read_snr(struct dvb_frontend* fe, u16* snr) |
505 | { |
506 | struct nxt6000_state* state = fe->demodulator_priv; |
507 | |
508 | *snr = nxt6000_readreg( state, OFDM_CHC_SNR) / 8; |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | static int nxt6000_read_ber(struct dvb_frontend* fe, u32* ber) |
514 | { |
515 | struct nxt6000_state* state = fe->demodulator_priv; |
516 | |
517 | nxt6000_writereg( state, VIT_COR_INTSTAT, data: 0x18 ); |
518 | |
519 | *ber = (nxt6000_readreg( state, VIT_BER_1 ) << 8 ) | |
520 | nxt6000_readreg( state, VIT_BER_0 ); |
521 | |
522 | nxt6000_writereg( state, VIT_COR_INTSTAT, data: 0x18); // Clear BER Done interrupts |
523 | |
524 | return 0; |
525 | } |
526 | |
527 | static int nxt6000_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) |
528 | { |
529 | struct nxt6000_state* state = fe->demodulator_priv; |
530 | |
531 | *signal_strength = (short) (511 - |
532 | (nxt6000_readreg(state, AGC_GAIN_1) + |
533 | ((nxt6000_readreg(state, AGC_GAIN_2) & 0x03) << 8))); |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) |
539 | { |
540 | tune->min_delay_ms = 500; |
541 | return 0; |
542 | } |
543 | |
544 | static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) |
545 | { |
546 | struct nxt6000_state* state = fe->demodulator_priv; |
547 | |
548 | if (enable) { |
549 | return nxt6000_writereg(state, ENABLE_TUNER_IIC, data: 0x01); |
550 | } else { |
551 | return nxt6000_writereg(state, ENABLE_TUNER_IIC, data: 0x00); |
552 | } |
553 | } |
554 | |
555 | static const struct dvb_frontend_ops nxt6000_ops; |
556 | |
557 | struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, |
558 | struct i2c_adapter* i2c) |
559 | { |
560 | struct nxt6000_state* state = NULL; |
561 | |
562 | /* allocate memory for the internal state */ |
563 | state = kzalloc(size: sizeof(struct nxt6000_state), GFP_KERNEL); |
564 | if (state == NULL) goto error; |
565 | |
566 | /* setup the state */ |
567 | state->config = config; |
568 | state->i2c = i2c; |
569 | |
570 | /* check if the demod is there */ |
571 | if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; |
572 | |
573 | /* create dvb_frontend */ |
574 | memcpy(&state->frontend.ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); |
575 | state->frontend.demodulator_priv = state; |
576 | return &state->frontend; |
577 | |
578 | error: |
579 | kfree(objp: state); |
580 | return NULL; |
581 | } |
582 | |
583 | static const struct dvb_frontend_ops nxt6000_ops = { |
584 | .delsys = { SYS_DVBT }, |
585 | .info = { |
586 | .name = "NxtWave NXT6000 DVB-T" , |
587 | .frequency_min_hz = 0, |
588 | .frequency_max_hz = 863250 * kHz, |
589 | .frequency_stepsize_hz = 62500, |
590 | /*.frequency_tolerance = *//* FIXME: 12% of SR */ |
591 | .symbol_rate_min = 0, /* FIXME */ |
592 | .symbol_rate_max = 9360000, /* FIXME */ |
593 | .symbol_rate_tolerance = 4000, |
594 | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
595 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | |
596 | FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | |
597 | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | |
598 | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | |
599 | FE_CAN_HIERARCHY_AUTO, |
600 | }, |
601 | |
602 | .release = nxt6000_release, |
603 | |
604 | .init = nxt6000_init, |
605 | .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, |
606 | |
607 | .get_tune_settings = nxt6000_fe_get_tune_settings, |
608 | |
609 | .set_frontend = nxt6000_set_frontend, |
610 | |
611 | .read_status = nxt6000_read_status, |
612 | .read_ber = nxt6000_read_ber, |
613 | .read_signal_strength = nxt6000_read_signal_strength, |
614 | .read_snr = nxt6000_read_snr, |
615 | }; |
616 | |
617 | module_param(debug, int, 0644); |
618 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)." ); |
619 | |
620 | MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver" ); |
621 | MODULE_AUTHOR("Florian Schirmer" ); |
622 | MODULE_LICENSE("GPL" ); |
623 | |
624 | EXPORT_SYMBOL_GPL(nxt6000_attach); |
625 | |