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