aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends/drx397xD.c
diff options
context:
space:
mode:
authorHenk Vergonet <henk.vergonet@gmail.com>2007-08-09 10:02:30 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-07-23 07:07:31 -0400
commit89f9257c06cb635ef140bd1acf21fb067ed4ed34 (patch)
treeaa951ad611fd60b9359b3ca625c2b5046eae39cc /drivers/media/dvb/frontends/drx397xD.c
parent14b395e35d1afdd8019d11b92e28041fad591b71 (diff)
V4L/DVB (7736): This patch adds support for the Micronas DRX3975D/DRX3977D DVB-T demodulator
The module needs an external firmware file. The module has been tested on a Pinnacle 330e, but with modules that are currently not part of the linux-dvb tree. So consider this highly experimental, don't use this code unless you are an experienced kernel developer. create mode 100644 drivers/media/dvb/frontends/drx397xD.c create mode 100644 drivers/media/dvb/frontends/drx397xD.h create mode 100644 drivers/media/dvb/frontends/drx397xD_fw.h Signed-off-by: Henk Vergonet <henk.vergonet@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/dvb/frontends/drx397xD.c')
-rw-r--r--drivers/media/dvb/frontends/drx397xD.c1505
1 files changed, 1505 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
new file mode 100644
index 000000000000..b0ff77ffc88b
--- /dev/null
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -0,0 +1,1505 @@
1/*
2 * Driver for Micronas drx397xD demodulator
3 *
4 * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define DEBUG /* uncomment if you want debugging output */
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/init.h>
25#include <linux/device.h>
26#include <linux/delay.h>
27#include <linux/string.h>
28#include <linux/firmware.h>
29
30#include "dvb_frontend.h"
31#include "drx397xD.h"
32
33static const char mod_name[] = "drx397xD";
34
35#define MAX_CLOCK_DRIFT 200 /* maximal 200 PPM allowed */
36
37#define F_SET_0D0h 1
38#define F_SET_0D4h 2
39
40typedef enum fw_ix {
41#define _FW_ENTRY(a, b) b
42#include "drx397xD_fw.h"
43} fw_ix_t;
44
45/* chip specifics */
46struct drx397xD_state {
47 struct i2c_adapter *i2c;
48 struct dvb_frontend frontend;
49 struct drx397xD_config config;
50 fw_ix_t chip_rev;
51 int flags;
52 u32 bandwidth_parm; /* internal bandwidth conversions */
53 u32 f_osc; /* w90: actual osc frequency [Hz] */
54};
55
56/*******************************************************************************
57 * Firmware
58 ******************************************************************************/
59
60static const char *blob_name[] = {
61#define _BLOB_ENTRY(a, b) a
62#include "drx397xD_fw.h"
63};
64
65typedef enum blob_ix {
66#define _BLOB_ENTRY(a, b) b
67#include "drx397xD_fw.h"
68} blob_ix_t;
69
70static struct {
71 const char *name;
72 const struct firmware *file;
73 rwlock_t lock;
74 int refcnt;
75 u8 *data[ARRAY_SIZE(blob_name)];
76} fw[] = {
77#define _FW_ENTRY(a, b) { \
78 .name = a, \
79 .file = 0, \
80 .lock = RW_LOCK_UNLOCKED, \
81 .refcnt = 0, \
82 .data = { } }
83#include "drx397xD_fw.h"
84};
85
86/* use only with writer lock aquired */
87static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix)
88{
89 memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
90 if (fw[ix].file)
91 release_firmware(fw[ix].file);
92}
93
94static void drx_release_fw(struct drx397xD_state *s)
95{
96 fw_ix_t ix = s->chip_rev;
97
98 pr_debug("%s\n", __FUNCTION__);
99
100 write_lock(&fw[ix].lock);
101 if (fw[ix].refcnt) {
102 fw[ix].refcnt--;
103 if (fw[ix].refcnt == 0)
104 _drx_release_fw(s, ix);
105 }
106 write_unlock(&fw[ix].lock);
107}
108
109static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix)
110{
111 u8 *data;
112 size_t size, len;
113 int i = 0, j, rc = -EINVAL;
114
115 pr_debug("%s\n", __FUNCTION__);
116
117 if (ix < 0 || ix >= ARRAY_SIZE(fw))
118 return -EINVAL;
119 s->chip_rev = ix;
120
121 write_lock(&fw[ix].lock);
122 if (fw[ix].file) {
123 rc = 0;
124 goto exit_ok;
125 }
126 memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
127
128 if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) {
129 printk(KERN_ERR "%s: Firmware \"%s\" not available\n",
130 mod_name, fw[ix].name);
131 rc = -ENOENT;
132 goto exit_err;
133 }
134
135 if (!fw[ix].file->data || fw[ix].file->size < 10)
136 goto exit_corrupt;
137
138 data = fw[ix].file->data;
139 size = fw[ix].file->size;
140
141 if (data[i++] != 2) /* check firmware version */
142 goto exit_corrupt;
143
144 do {
145 switch (data[i++]) {
146 case 0x00: /* bytecode */
147 if (i >= size)
148 break;
149 i += data[i];
150 case 0x01: /* reset */
151 case 0x02: /* sleep */
152 i++;
153 break;
154 case 0xfe: /* name */
155 len = strnlen(&data[i], size - i);
156 if (i + len + 1 >= size)
157 goto exit_corrupt;
158 if (data[i + len + 1] != 0)
159 goto exit_corrupt;
160 for (j = 0; j < ARRAY_SIZE(blob_name); j++) {
161 if (strcmp(blob_name[j], &data[i]) == 0) {
162 fw[ix].data[j] = &data[i + len + 1];
163 pr_debug("Loading %s\n", blob_name[j]);
164 }
165 }
166 i += len + 1;
167 break;
168 case 0xff: /* file terminator */
169 if (i == size) {
170 rc = 0;
171 goto exit_ok;
172 }
173 default:
174 goto exit_corrupt;
175 }
176 } while (i < size);
177 exit_corrupt:
178 printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name);
179 exit_err:
180 _drx_release_fw(s, ix);
181 fw[ix].refcnt--;
182 exit_ok:
183 fw[ix].refcnt++;
184 write_unlock(&fw[ix].lock);
185 return rc;
186}
187
188/*******************************************************************************
189 * i2c bus IO
190 ******************************************************************************/
191
192static int write_fw(struct drx397xD_state *s, blob_ix_t ix)
193{
194 struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 };
195 u8 *data;
196 int len, rc = 0, i = 0;
197
198 if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) {
199 pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__);
200 return -EINVAL;
201 }
202 pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]);
203
204 read_lock(&fw[s->chip_rev].lock);
205 data = fw[s->chip_rev].data[ix];
206 if (!data) {
207 rc = -EINVAL;
208 goto exit_rc;
209 }
210
211 for (;;) {
212 switch (data[i++]) {
213 case 0: /* bytecode */
214 len = data[i++];
215 msg.len = len;
216 msg.buf = &data[i];
217 if (i2c_transfer(s->i2c, &msg, 1) != 1) {
218 rc = -EIO;
219 goto exit_rc;
220 }
221 i += len;
222 break;
223 case 1: /* reset */
224 case 2: /* sleep */
225 i++;
226 break;
227 default:
228 goto exit_rc;
229 }
230 }
231 exit_rc:
232 read_unlock(&fw[s->chip_rev].lock);
233 return 0;
234}
235
236/* Function is not endian safe, use the RD16 wrapper below */
237static int _read16(struct drx397xD_state *s, u32 i2c_adr)
238{
239 int rc;
240 u8 a[4];
241 u16 v;
242 struct i2c_msg msg[2] = {
243 {
244 .addr = s->config.demod_address,
245 .flags = 0,
246 .buf = a,
247 .len = sizeof(a)
248 }
249 , {
250 .addr = s->config.demod_address,
251 .flags = I2C_M_RD,
252 .buf = (u8 *) & v,
253 .len = sizeof(v)
254 }
255 };
256
257 *(u32 *) a = i2c_adr;
258
259 rc = i2c_transfer(s->i2c, msg, 2);
260 if (rc != 2)
261 return -EIO;
262
263 return le16_to_cpu(v);
264}
265
266/* Function is not endian safe, use the WR16.. wrappers below */
267static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val)
268{
269 u8 a[6];
270 int rc;
271 struct i2c_msg msg = {
272 .addr = s->config.demod_address,
273 .flags = 0,
274 .buf = a,
275 .len = sizeof(a)
276 };
277
278 *(u32 *) a = i2c_adr;
279 *(u16 *) & a[4] = val;
280
281 rc = i2c_transfer(s->i2c, &msg, 1);
282 if (rc != 1)
283 return -EIO;
284 return 0;
285}
286
287#define WR16(ss,adr, val) \
288 _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val))
289#define WR16_E0(ss,adr, val) \
290 _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val))
291#define RD16(ss,adr) \
292 _read16(ss, I2C_ADR_C0(adr))
293
294#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc
295
296/*******************************************************************************
297 * Tuner callback
298 ******************************************************************************/
299
300static int PLL_Set(struct drx397xD_state *s,
301 struct dvb_frontend_parameters *fep, int *df_tuner)
302{
303 struct dvb_frontend *fe = &s->frontend;
304 u32 f_tuner, f = fep->frequency;
305 int rc;
306
307 pr_debug("%s\n", __FUNCTION__);
308
309 if ((f > s->frontend.ops.tuner_ops.info.frequency_max) ||
310 (f < s->frontend.ops.tuner_ops.info.frequency_min))
311 return -EINVAL;
312
313 *df_tuner = 0;
314 if (!s->frontend.ops.tuner_ops.set_params ||
315 !s->frontend.ops.tuner_ops.get_frequency)
316 return -ENOSYS;
317
318 rc = s->frontend.ops.tuner_ops.set_params(fe, fep);
319 if (rc < 0)
320 return rc;
321
322 rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner);
323 if (rc < 0)
324 return rc;
325
326 *df_tuner = f_tuner - f;
327 pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f,
328 f_tuner);
329
330 return 0;
331}
332
333/*******************************************************************************
334 * Demodulator helper functions
335 ******************************************************************************/
336
337static int SC_WaitForReady(struct drx397xD_state *s)
338{
339 int cnt = 1000;
340 int rc;
341
342 pr_debug("%s\n", __FUNCTION__);
343
344 while (cnt--) {
345 rc = RD16(s, 0x820043);
346 if (rc == 0)
347 return 0;
348 }
349 return -1;
350}
351
352static int SC_SendCommand(struct drx397xD_state *s, int cmd)
353{
354 int rc;
355
356 pr_debug("%s\n", __FUNCTION__);
357
358 WR16(s, 0x820043, cmd);
359 SC_WaitForReady(s);
360 rc = RD16(s, 0x820042);
361 if ((rc & 0xffff) == 0xffff)
362 return -1;
363 return 0;
364}
365
366static int HI_Command(struct drx397xD_state *s, u16 cmd)
367{
368 int rc, cnt = 1000;
369
370 pr_debug("%s\n", __FUNCTION__);
371
372 rc = WR16(s, 0x420032, cmd);
373 if (rc < 0)
374 return rc;
375
376 do {
377 rc = RD16(s, 0x420032);
378 if (rc == 0) {
379 rc = RD16(s, 0x420031);
380 return rc;
381 }
382 if (rc < 0)
383 return rc;
384 } while (--cnt);
385 return rc;
386}
387
388static int HI_CfgCommand(struct drx397xD_state *s)
389{
390
391 pr_debug("%s\n", __FUNCTION__);
392
393 WR16(s, 0x420033, 0x3973);
394 WR16(s, 0x420034, s->config.w50); // code 4, log 4
395 WR16(s, 0x420035, s->config.w52); // code 15, log 9
396 WR16(s, 0x420036, s->config.demod_address << 1);
397 WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1
398// WR16(s, 0x420033, 0x3973);
399 if ((s->config.w56 & 8) == 0)
400 return HI_Command(s, 3);
401 return WR16(s, 0x420032, 0x3);
402}
403
404static const u8 fastIncrDecLUT_15273[] = {
405 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14,
406 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f
407};
408
409static const u8 slowIncrDecLUT_15272[] = {
410 3, 4, 4, 5, 6
411};
412
413static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc)
414{
415 u16 w06 = agc->w06;
416 u16 w08 = agc->w08;
417 u16 w0A = agc->w0A;
418 u16 w0C = agc->w0C;
419 int quot, rem, i, rc = -EINVAL;
420
421 pr_debug("%s\n", __FUNCTION__);
422
423 if (agc->w04 > 0x3ff)
424 goto exit_rc;
425
426 if (agc->d00 == 1) {
427 EXIT_RC(RD16(s, 0x0c20010));
428 rc &= ~0x10;
429 EXIT_RC(WR16(s, 0x0c20010, rc));
430 return WR16(s, 0x0c20030, agc->w04 & 0x7ff);
431 }
432
433 if (agc->d00 != 0)
434 goto exit_rc;
435 if (w0A < w08)
436 goto exit_rc;
437 if (w0A > 0x3ff)
438 goto exit_rc;
439 if (w0C > 0x3ff)
440 goto exit_rc;
441 if (w06 > 0x3ff)
442 goto exit_rc;
443
444 EXIT_RC(RD16(s, 0x0c20010));
445 rc |= 0x10;
446 EXIT_RC(WR16(s, 0x0c20010, rc));
447
448 EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff));
449 EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1));
450 EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff));
451
452 quot = w0C / 113;
453 rem = w0C % 113;
454 if (quot <= 8) {
455 quot = 8 - quot;
456 } else {
457 quot = 0;
458 rem += 113;
459 }
460
461 EXIT_RC(WR16(s, 0x0c20024, quot));
462
463 i = fastIncrDecLUT_15273[rem / 8];
464 EXIT_RC(WR16(s, 0x0c2002d, i));
465 EXIT_RC(WR16(s, 0x0c2002e, i));
466
467 i = slowIncrDecLUT_15272[rem / 28];
468 EXIT_RC(WR16(s, 0x0c2002b, i));
469 rc = WR16(s, 0x0c2002c, i);
470 exit_rc:
471 return rc;
472}
473
474static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc)
475{
476 u16 w04 = agc->w04;
477 u16 w06 = agc->w06;
478 int rc = -1;
479
480 pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06);
481
482 if (w04 > 0x3ff)
483 goto exit_rc;
484
485 switch (agc->d00) {
486 case 1:
487 if (w04 == 0x3ff)
488 w04 = 0x400;
489
490 EXIT_RC(WR16(s, 0x0c20036, w04));
491 s->config.w9C &= ~2;
492 EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
493 EXIT_RC(RD16(s, 0x0c20010));
494 rc &= 0xbfdf;
495 EXIT_RC(WR16(s, 0x0c20010, rc));
496 EXIT_RC(RD16(s, 0x0c20013));
497 rc &= ~2;
498 break;
499 case 0:
500 // loc_8000659
501 s->config.w9C &= ~2;
502 EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
503 EXIT_RC(RD16(s, 0x0c20010));
504 rc &= 0xbfdf;
505 rc |= 0x4000;
506 EXIT_RC(WR16(s, 0x0c20010, rc));
507 EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f));
508 EXIT_RC(RD16(s, 0x0c20013));
509 rc &= ~2;
510 break;
511 default:
512 s->config.w9C |= 2;
513 EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
514 EXIT_RC(RD16(s, 0x0c20010));
515 rc &= 0xbfdf;
516 EXIT_RC(WR16(s, 0x0c20010, rc));
517
518 EXIT_RC(WR16(s, 0x0c20036, 0));
519
520 EXIT_RC(RD16(s, 0x0c20013));
521 rc |= 2;
522 }
523 rc = WR16(s, 0x0c20013, rc);
524 exit_rc:
525 return rc;
526}
527
528static int GetLockStatus(struct drx397xD_state *s, int *lockstat)
529{
530 int rc;
531
532 *lockstat = 0;
533
534 rc = RD16(s, 0x082004b);
535 if (rc < 0)
536 return rc;
537
538 if (s->config.d60 != 2)
539 return 0;
540
541 if ((rc & 7) == 7)
542 *lockstat |= 1;
543 if ((rc & 3) == 3)
544 *lockstat |= 2;
545 if (rc & 1)
546 *lockstat |= 4;
547 return 0;
548}
549
550static int CorrectSysClockDeviation(struct drx397xD_state *s)
551{
552 int rc = -EINVAL;
553 int lockstat;
554 u32 clk, clk_limit;
555
556 pr_debug("%s\n", __FUNCTION__);
557
558 if (s->config.d5C == 0) {
559 EXIT_RC(WR16(s, 0x08200e8, 0x010));
560 EXIT_RC(WR16(s, 0x08200e9, 0x113));
561 s->config.d5C = 1;
562 return rc;
563 }
564 if (s->config.d5C != 1)
565 goto exit_rc;
566
567 rc = RD16(s, 0x0820048);
568
569 rc = GetLockStatus(s, &lockstat);
570 if (rc < 0)
571 goto exit_rc;
572 if ((lockstat & 1) == 0)
573 goto exit_rc;
574
575 EXIT_RC(WR16(s, 0x0420033, 0x200));
576 EXIT_RC(WR16(s, 0x0420034, 0xc5));
577 EXIT_RC(WR16(s, 0x0420035, 0x10));
578 EXIT_RC(WR16(s, 0x0420036, 0x1));
579 EXIT_RC(WR16(s, 0x0420037, 0xa));
580 EXIT_RC(HI_Command(s, 6));
581 EXIT_RC(RD16(s, 0x0420040));
582 clk = rc;
583 EXIT_RC(RD16(s, 0x0420041));
584 clk |= rc << 16;
585
586 if (clk <= 0x26ffff)
587 goto exit_rc;
588 if (clk > 0x610000)
589 goto exit_rc;
590
591 if (!s->bandwidth_parm)
592 return -EINVAL;
593
594 /* round & convert to Hz */
595 clk = ((u64) (clk + 0x800000) * s->bandwidth_parm + (1 << 20)) >> 21;
596 clk_limit = s->config.f_osc * MAX_CLOCK_DRIFT / 1000;
597
598 if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) {
599 s->f_osc = clk;
600 pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__,
601 s->config.f_osc * 1000, clk - s->config.f_osc * 1000);
602 }
603 rc = WR16(s, 0x08200e8, 0);
604 exit_rc:
605 return rc;
606}
607
608static int ConfigureMPEGOutput(struct drx397xD_state *s, int type)
609{
610 int rc, si, bp;
611
612 pr_debug("%s\n", __FUNCTION__);
613
614 si = s->config.wA0;
615 if (s->config.w98 == 0) {
616 si |= 1;
617 bp = 0;
618 } else {
619 si &= ~1;
620 bp = 0x200;
621 }
622 if (s->config.w9A == 0) {
623 si |= 0x80;
624 } else {
625 si &= ~0x80;
626 }
627
628 EXIT_RC(WR16(s, 0x2150045, 0));
629 EXIT_RC(WR16(s, 0x2150010, si));
630 EXIT_RC(WR16(s, 0x2150011, bp));
631 rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0));
632 exit_rc:
633 return rc;
634}
635
636static int drx_tune(struct drx397xD_state *s,
637 struct dvb_frontend_parameters *fep)
638{
639 u16 v22 = 0;
640 u16 v1C = 0;
641 u16 v1A = 0;
642 u16 v18 = 0;
643 u32 edi = 0, ebx = 0, ebp = 0, edx = 0;
644 u16 v20 = 0, v1E = 0, v16 = 0, v14 = 0, v12 = 0, v10 = 0, v0E = 0;
645
646 int rc, df_tuner;
647 int a, b, c, d;
648 pr_debug("%s %d\n", __FUNCTION__, s->config.d60);
649
650 if (s->config.d60 != 2)
651 goto set_tuner;
652 rc = CorrectSysClockDeviation(s);
653 if (rc < 0)
654 goto set_tuner;
655
656 s->config.d60 = 1;
657 rc = ConfigureMPEGOutput(s, 0);
658 if (rc < 0)
659 goto set_tuner;
660 set_tuner:
661
662 rc = PLL_Set(s, fep, &df_tuner);
663 if (rc < 0) {
664 printk(KERN_ERR "Error in pll_set\n");
665 goto exit_rc;
666 }
667 msleep(200);
668
669 a = rc = RD16(s, 0x2150016);
670 if (rc < 0)
671 goto exit_rc;
672 b = rc = RD16(s, 0x2150010);
673 if (rc < 0)
674 goto exit_rc;
675 c = rc = RD16(s, 0x2150034);
676 if (rc < 0)
677 goto exit_rc;
678 d = rc = RD16(s, 0x2150035);
679 if (rc < 0)
680 goto exit_rc;
681 rc = WR16(s, 0x2150014, c);
682 rc = WR16(s, 0x2150015, d);
683 rc = WR16(s, 0x2150010, 0);
684 rc = WR16(s, 0x2150000, 2);
685 rc = WR16(s, 0x2150036, 0x0fff);
686 rc = WR16(s, 0x2150016, a);
687
688 rc = WR16(s, 0x2150010, 2);
689 rc = WR16(s, 0x2150007, 0);
690 rc = WR16(s, 0x2150000, 1);
691 rc = WR16(s, 0x2110000, 0);
692 rc = WR16(s, 0x0800000, 0);
693 rc = WR16(s, 0x2800000, 0);
694 rc = WR16(s, 0x2110010, 0x664);
695
696 rc = write_fw(s, DRXD_ResetECRAM);
697 rc = WR16(s, 0x2110000, 1);
698
699 rc = write_fw(s, DRXD_InitSC);
700 if (rc < 0)
701 goto exit_rc;
702
703 rc = SetCfgIfAgc(s, &s->config.ifagc);
704 if (rc < 0)
705 goto exit_rc;
706
707 rc = SetCfgRfAgc(s, &s->config.rfagc);
708 if (rc < 0)
709 goto exit_rc;
710
711 if (fep->u.ofdm.transmission_mode != TRANSMISSION_MODE_2K)
712 v22 = 1;
713 switch (fep->u.ofdm.transmission_mode) {
714 case TRANSMISSION_MODE_8K:
715 edi = 1;
716 if (s->chip_rev == DRXD_FW_B1)
717 break;
718
719 rc = WR16(s, 0x2010010, 0);
720 if (rc < 0)
721 break;
722 v1C = 0x63;
723 v1A = 0x53;
724 v18 = 0x43;
725 break;
726 default:
727 edi = 0;
728 if (s->chip_rev == DRXD_FW_B1)
729 break;
730
731 rc = WR16(s, 0x2010010, 1);
732 if (rc < 0)
733 break;
734
735 v1C = 0x61;
736 v1A = 0x47;
737 v18 = 0x41;
738 }
739
740 switch (fep->u.ofdm.guard_interval) {
741 case GUARD_INTERVAL_1_4:
742 edi |= 0x0c;
743 break;
744 case GUARD_INTERVAL_1_8:
745 edi |= 0x08;
746 break;
747 case GUARD_INTERVAL_1_16:
748 edi |= 0x04;
749 break;
750 case GUARD_INTERVAL_1_32:
751 break;
752 default:
753 v22 |= 2;
754 }
755
756 ebx = 0;
757 ebp = 0;
758 v20 = 0;
759 v1E = 0;
760 v16 = 0;
761 v14 = 0;
762 v12 = 0;
763 v10 = 0;
764 v0E = 0;
765
766 switch (fep->u.ofdm.hierarchy_information) {
767 case HIERARCHY_1:
768 edi |= 0x40;
769 if (s->chip_rev == DRXD_FW_B1)
770 break;
771 rc = WR16(s, 0x1c10047, 1);
772 if (rc < 0)
773 goto exit_rc;
774 rc = WR16(s, 0x2010012, 1);
775 if (rc < 0)
776 goto exit_rc;
777 ebx = 0x19f;
778 ebp = 0x1fb;
779 v20 = 0x0c0;
780 v1E = 0x195;
781 v16 = 0x1d6;
782 v14 = 0x1ef;
783 v12 = 4;
784 v10 = 5;
785 v0E = 5;
786 break;
787 case HIERARCHY_2:
788 edi |= 0x80;
789 if (s->chip_rev == DRXD_FW_B1)
790 break;
791 rc = WR16(s, 0x1c10047, 2);
792 if (rc < 0)
793 goto exit_rc;
794 rc = WR16(s, 0x2010012, 2);
795 if (rc < 0)
796 goto exit_rc;
797 ebx = 0x08f;
798 ebp = 0x12f;
799 v20 = 0x0c0;
800 v1E = 0x11e;
801 v16 = 0x1d6;
802 v14 = 0x15e;
803 v12 = 4;
804 v10 = 5;
805 v0E = 5;
806 break;
807 case HIERARCHY_4:
808 edi |= 0xc0;
809 if (s->chip_rev == DRXD_FW_B1)
810 break;
811 rc = WR16(s, 0x1c10047, 3);
812 if (rc < 0)
813 goto exit_rc;
814 rc = WR16(s, 0x2010012, 3);
815 if (rc < 0)
816 goto exit_rc;
817 ebx = 0x14d;
818 ebp = 0x197;
819 v20 = 0x0c0;
820 v1E = 0x1ce;
821 v16 = 0x1d6;
822 v14 = 0x11a;
823 v12 = 4;
824 v10 = 6;
825 v0E = 5;
826 break;
827 default:
828 v22 |= 8;
829 if (s->chip_rev == DRXD_FW_B1)
830 break;
831 rc = WR16(s, 0x1c10047, 0);
832 if (rc < 0)
833 goto exit_rc;
834 rc = WR16(s, 0x2010012, 0);
835 if (rc < 0)
836 goto exit_rc;
837 // QPSK QAM16 QAM64
838 ebx = 0x19f; // 62
839 ebp = 0x1fb; // 15
840 v20 = 0x16a; // 62
841 v1E = 0x195; // 62
842 v16 = 0x1bb; // 15
843 v14 = 0x1ef; // 15
844 v12 = 5; // 16
845 v10 = 5; // 16
846 v0E = 5; // 16
847 }
848
849 switch (fep->u.ofdm.constellation) {
850 default:
851 v22 |= 4;
852 case QPSK:
853 if (s->chip_rev == DRXD_FW_B1)
854 break;
855
856 rc = WR16(s, 0x1c10046, 0);
857 if (rc < 0)
858 goto exit_rc;
859 rc = WR16(s, 0x2010011, 0);
860 if (rc < 0)
861 goto exit_rc;
862 rc = WR16(s, 0x201001a, 0x10);
863 if (rc < 0)
864 goto exit_rc;
865 rc = WR16(s, 0x201001b, 0);
866 if (rc < 0)
867 goto exit_rc;
868 rc = WR16(s, 0x201001c, 0);
869 if (rc < 0)
870 goto exit_rc;
871 rc = WR16(s, 0x1c10062, v20);
872 if (rc < 0)
873 goto exit_rc;
874 rc = WR16(s, 0x1c1002a, v1C);
875 if (rc < 0)
876 goto exit_rc;
877 rc = WR16(s, 0x1c10015, v16);
878 if (rc < 0)
879 goto exit_rc;
880 rc = WR16(s, 0x1c10016, v12);
881 if (rc < 0)
882 goto exit_rc;
883 break;
884 case QAM_16:
885 edi |= 0x10;
886 if (s->chip_rev == DRXD_FW_B1)
887 break;
888
889 rc = WR16(s, 0x1c10046, 1);
890 if (rc < 0)
891 goto exit_rc;
892 rc = WR16(s, 0x2010011, 1);
893 if (rc < 0)
894 goto exit_rc;
895 rc = WR16(s, 0x201001a, 0x10);
896 if (rc < 0)
897 goto exit_rc;
898 rc = WR16(s, 0x201001b, 4);
899 if (rc < 0)
900 goto exit_rc;
901 rc = WR16(s, 0x201001c, 0);
902 if (rc < 0)
903 goto exit_rc;
904 rc = WR16(s, 0x1c10062, v1E);
905 if (rc < 0)
906 goto exit_rc;
907 rc = WR16(s, 0x1c1002a, v1A);
908 if (rc < 0)
909 goto exit_rc;
910 rc = WR16(s, 0x1c10015, v14);
911 if (rc < 0)
912 goto exit_rc;
913 rc = WR16(s, 0x1c10016, v10);
914 if (rc < 0)
915 goto exit_rc;
916 break;
917 case QAM_64:
918 edi |= 0x20;
919 rc = WR16(s, 0x1c10046, 2);
920 if (rc < 0)
921 goto exit_rc;
922 rc = WR16(s, 0x2010011, 2);
923 if (rc < 0)
924 goto exit_rc;
925 rc = WR16(s, 0x201001a, 0x20);
926 if (rc < 0)
927 goto exit_rc;
928 rc = WR16(s, 0x201001b, 8);
929 if (rc < 0)
930 goto exit_rc;
931 rc = WR16(s, 0x201001c, 2);
932 if (rc < 0)
933 goto exit_rc;
934 rc = WR16(s, 0x1c10062, ebx);
935 if (rc < 0)
936 goto exit_rc;
937 rc = WR16(s, 0x1c1002a, v18);
938 if (rc < 0)
939 goto exit_rc;
940 rc = WR16(s, 0x1c10015, ebp);
941 if (rc < 0)
942 goto exit_rc;
943 rc = WR16(s, 0x1c10016, v0E);
944 if (rc < 0)
945 goto exit_rc;
946 break;
947 }
948
949 if (s->config.s20d24 == 1) {
950 rc = WR16(s, 0x2010013, 0);
951 } else {
952 rc = WR16(s, 0x2010013, 1);
953 edi |= 0x1000;
954 }
955
956 switch (fep->u.ofdm.code_rate_HP) {
957 default:
958 v22 |= 0x10;
959 case FEC_1_2:
960 if (s->chip_rev == DRXD_FW_B1)
961 break;
962 rc = WR16(s, 0x2090011, 0);
963 break;
964 case FEC_2_3:
965 edi |= 0x200;
966 if (s->chip_rev == DRXD_FW_B1)
967 break;
968 rc = WR16(s, 0x2090011, 1);
969 break;
970 case FEC_3_4:
971 edi |= 0x400;
972 if (s->chip_rev == DRXD_FW_B1)
973 break;
974 rc = WR16(s, 0x2090011, 2);
975 break;
976 case FEC_5_6: /* 5 */
977 edi |= 0x600;
978 if (s->chip_rev == DRXD_FW_B1)
979 break;
980 rc = WR16(s, 0x2090011, 3);
981 break;
982 case FEC_7_8: /* 7 */
983 edi |= 0x800;
984 if (s->chip_rev == DRXD_FW_B1)
985 break;
986 rc = WR16(s, 0x2090011, 4);
987 break;
988 };
989 if (rc < 0)
990 goto exit_rc;
991
992 switch (fep->u.ofdm.bandwidth) {
993 default:
994 rc = -EINVAL;
995 goto exit_rc;
996 case BANDWIDTH_8_MHZ: /* 0 */
997 case BANDWIDTH_AUTO:
998 rc = WR16(s, 0x0c2003f, 0x32);
999 s->bandwidth_parm = ebx = 0x8b8249; // 9142857
1000 edx = 0;
1001 break;
1002 case BANDWIDTH_7_MHZ:
1003 rc = WR16(s, 0x0c2003f, 0x3b);
1004 s->bandwidth_parm = ebx = 0x7a1200; // 8000000
1005 edx = 0x4807;
1006 break;
1007 case BANDWIDTH_6_MHZ:
1008 rc = WR16(s, 0x0c2003f, 0x47);
1009 s->bandwidth_parm = ebx = 0x68a1b6; // 6857142
1010 edx = 0x0f07;
1011 break;
1012 };
1013
1014 if (rc < 0)
1015 goto exit_rc;
1016
1017 rc = WR16(s, 0x08200ec, edx);
1018 if (rc < 0)
1019 goto exit_rc;
1020
1021 rc = RD16(s, 0x0820050);
1022 if (rc < 0)
1023 goto exit_rc;
1024 rc = WR16(s, 0x0820050, rc);
1025
1026 {
1027 long dummy;
1028
1029 /* Configure bandwidth specific factor */
1030 ebx = div_ll_X_l_rem(((u64) (s->f_osc) << 21) + (ebx >> 1),
1031 ebx, &dummy) - 0x800000;
1032 EXIT_RC(WR16(s, 0x0c50010, ebx & 0xffff));
1033 EXIT_RC(WR16(s, 0x0c50011, ebx >> 16));
1034
1035 /* drx397xD oscillator calibration */
1036 ebx = div_ll_X_l_rem(((u64) (s->config.f_if + df_tuner) << 28) +
1037 (s->f_osc >> 1), s->f_osc, &dummy);
1038 }
1039 ebx &= 0xfffffff;
1040 if (fep->inversion == INVERSION_ON)
1041 ebx = 0x10000000 - ebx;
1042
1043 EXIT_RC(WR16(s, 0x0c30010, ebx & 0xffff));
1044 EXIT_RC(WR16(s, 0x0c30011, ebx >> 16));
1045
1046 EXIT_RC(WR16(s, 0x0800000, 1));
1047 EXIT_RC(RD16(s, 0x0800000));
1048
1049
1050 EXIT_RC(SC_WaitForReady(s));
1051 EXIT_RC(WR16(s, 0x0820042, 0));
1052 EXIT_RC(WR16(s, 0x0820041, v22));
1053 EXIT_RC(WR16(s, 0x0820040, edi));
1054 EXIT_RC(SC_SendCommand(s, 3));
1055
1056 rc = RD16(s, 0x0800000);
1057
1058 SC_WaitForReady(s);
1059 WR16(s, 0x0820042, 0);
1060 WR16(s, 0x0820041, 1);
1061 WR16(s, 0x0820040, 1);
1062 SC_SendCommand(s, 1);
1063
1064// rc = WR16(s, 0x2150000, 1);
1065// if (rc < 0) goto exit_rc;
1066
1067 rc = WR16(s, 0x2150000, 2);
1068 rc = WR16(s, 0x2150016, a);
1069 rc = WR16(s, 0x2150010, 4);
1070 rc = WR16(s, 0x2150036, 0);
1071 rc = WR16(s, 0x2150000, 1);
1072 s->config.d60 = 2;
1073 exit_rc:
1074 return rc;
1075}
1076
1077/*******************************************************************************
1078 * DVB interface
1079 ******************************************************************************/
1080
1081static int drx397x_init(struct dvb_frontend *fe)
1082{
1083 struct drx397xD_state *s = fe->demodulator_priv;
1084 int rc;
1085
1086 pr_debug("%s\n", __FUNCTION__);
1087
1088 s->config.rfagc.d00 = 2; /* 0x7c */
1089 s->config.rfagc.w04 = 0;
1090 s->config.rfagc.w06 = 0x3ff;
1091
1092 s->config.ifagc.d00 = 0; /* 0x68 */
1093 s->config.ifagc.w04 = 0;
1094 s->config.ifagc.w06 = 140;
1095 s->config.ifagc.w08 = 0;
1096 s->config.ifagc.w0A = 0x3ff;
1097 s->config.ifagc.w0C = 0x388;
1098
1099 /* for signal strenght calculations */
1100 s->config.ss76 = 820;
1101 s->config.ss78 = 2200;
1102 s->config.ss7A = 150;
1103
1104 /* HI_CfgCommand */
1105 s->config.w50 = 4;
1106 s->config.w52 = 9; // 0xf;
1107
1108 s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */
1109 s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */
1110 s->config.w92 = 12000; // 20000;
1111
1112 s->config.w9C = 0x000e;
1113 s->config.w9E = 0x0000;
1114
1115 /* ConfigureMPEGOutput params */
1116 s->config.wA0 = 4;
1117 s->config.w98 = 1; // 0;
1118 s->config.w9A = 1;
1119
1120 /* get chip revision */
1121 rc = RD16(s, 0x2410019);
1122 if (rc < 0)
1123 return -ENODEV;
1124
1125 if (rc == 0) {
1126 printk(KERN_INFO "%s: chip revision A2\n", mod_name);
1127 rc = drx_load_fw(s, DRXD_FW_A2);
1128 } else {
1129
1130 rc = (rc >> 12) - 3;
1131 switch (rc) {
1132 case 1:
1133 s->flags |= F_SET_0D4h;
1134 case 0:
1135 case 4:
1136 s->flags |= F_SET_0D0h;
1137 break;
1138 case 2:
1139 case 5:
1140 break;
1141 case 3:
1142 s->flags |= F_SET_0D4h;
1143 break;
1144 default:
1145 return -ENODEV;
1146 };
1147 printk(KERN_INFO "%s: chip revision B1.%d\n", mod_name, rc);
1148 rc = drx_load_fw(s, DRXD_FW_B1);
1149 }
1150 if (rc < 0)
1151 goto error;
1152
1153 rc = WR16(s, 0x0420033, 0x3973);
1154 if (rc < 0)
1155 goto error;
1156
1157 rc = HI_Command(s, 2);
1158
1159 msleep(1);
1160
1161 if (s->chip_rev == DRXD_FW_A2) {
1162 rc = WR16(s, 0x043012d, 0x47F);
1163 if (rc < 0)
1164 goto error;
1165 }
1166 rc = WR16_E0(s, 0x0400000, 0);
1167 if (rc < 0)
1168 goto error;
1169
1170 if (s->config.w92 > 20000 || s->config.w92 % 4000) {
1171 printk(KERN_ERR "%s: invalid osc frequency\n", mod_name);
1172 rc = -1;
1173 goto error;
1174 }
1175
1176 rc = WR16(s, 0x2410010, 1);
1177 if (rc < 0)
1178 goto error;
1179 rc = WR16(s, 0x2410011, 0x15);
1180 if (rc < 0)
1181 goto error;
1182 rc = WR16(s, 0x2410012, s->config.w92 / 4000);
1183 if (rc < 0)
1184 goto error;
1185#ifdef ORIG_FW
1186 rc = WR16(s, 0x2410015, 2);
1187 if (rc < 0)
1188 goto error;
1189#endif
1190 rc = WR16(s, 0x2410017, 0x3973);
1191 if (rc < 0)
1192 goto error;
1193
1194 s->f_osc = s->config.f_osc * 1000; /* initial estimator */
1195
1196 s->config.w56 = 1;
1197
1198 rc = HI_CfgCommand(s);
1199 if (rc < 0)
1200 goto error;
1201
1202 rc = write_fw(s, DRXD_InitAtomicRead);
1203 if (rc < 0)
1204 goto error;
1205
1206 if (s->chip_rev == DRXD_FW_A2) {
1207 rc = WR16(s, 0x2150013, 0);
1208 if (rc < 0)
1209 goto error;
1210 }
1211
1212 rc = WR16_E0(s, 0x0400002, 0);
1213 if (rc < 0)
1214 goto error;
1215 rc = WR16(s, 0x0400002, 0);
1216 if (rc < 0)
1217 goto error;
1218
1219 if (s->chip_rev == DRXD_FW_A2) {
1220 rc = write_fw(s, DRXD_ResetCEFR);
1221 if (rc < 0)
1222 goto error;
1223 }
1224 rc = write_fw(s, DRXD_microcode);
1225 if (rc < 0)
1226 goto error;
1227
1228 s->config.w9C = 0x0e;
1229 if (s->flags & F_SET_0D0h) {
1230 s->config.w9C = 0;
1231 rc = RD16(s, 0x0c20010);
1232 if (rc < 0)
1233 goto write_DRXD_InitFE_1;
1234
1235 rc &= ~0x1000;
1236 rc = WR16(s, 0x0c20010, rc);
1237 if (rc < 0)
1238 goto write_DRXD_InitFE_1;
1239
1240 rc = RD16(s, 0x0c20011);
1241 if (rc < 0)
1242 goto write_DRXD_InitFE_1;
1243
1244 rc &= ~0x8;
1245 rc = WR16(s, 0x0c20011, rc);
1246 if (rc < 0)
1247 goto write_DRXD_InitFE_1;
1248
1249 rc = WR16(s, 0x0c20012, 1);
1250 }
1251
1252 write_DRXD_InitFE_1:
1253
1254 rc = write_fw(s, DRXD_InitFE_1);
1255 if (rc < 0)
1256 goto error;
1257
1258 rc = 1;
1259 if (s->chip_rev == DRXD_FW_B1) {
1260 if (s->flags & F_SET_0D0h)
1261 rc = 0;
1262 } else {
1263 if (s->flags & F_SET_0D0h)
1264 rc = 4;
1265 }
1266
1267 rc = WR16(s, 0x0C20012, rc);
1268 if (rc < 0)
1269 goto error;
1270
1271 rc = WR16(s, 0x0C20013, s->config.w9E);
1272 if (rc < 0)
1273 goto error;
1274 rc = WR16(s, 0x0C20015, s->config.w9C);
1275 if (rc < 0)
1276 goto error;
1277
1278 rc = write_fw(s, DRXD_InitFE_2);
1279 if (rc < 0)
1280 goto error;
1281 rc = write_fw(s, DRXD_InitFT);
1282 if (rc < 0)
1283 goto error;
1284 rc = write_fw(s, DRXD_InitCP);
1285 if (rc < 0)
1286 goto error;
1287 rc = write_fw(s, DRXD_InitCE);
1288 if (rc < 0)
1289 goto error;
1290 rc = write_fw(s, DRXD_InitEQ);
1291 if (rc < 0)
1292 goto error;
1293 rc = write_fw(s, DRXD_InitEC);
1294 if (rc < 0)
1295 goto error;
1296 rc = write_fw(s, DRXD_InitSC);
1297 if (rc < 0)
1298 goto error;
1299
1300 rc = SetCfgIfAgc(s, &s->config.ifagc);
1301 if (rc < 0)
1302 goto error;
1303
1304 rc = SetCfgRfAgc(s, &s->config.rfagc);
1305 if (rc < 0)
1306 goto error;
1307
1308 rc = ConfigureMPEGOutput(s, 1);
1309 rc = WR16(s, 0x08201fe, 0x0017);
1310 rc = WR16(s, 0x08201ff, 0x0101);
1311
1312 s->config.d5C = 0;
1313 s->config.d60 = 1;
1314 s->config.d48 = 1;
1315 error:
1316 return rc;
1317}
1318
1319static int drx397x_get_frontend(struct dvb_frontend *fe,
1320 struct dvb_frontend_parameters *params)
1321{
1322 return 0;
1323}
1324
1325static int drx397x_set_frontend(struct dvb_frontend *fe,
1326 struct dvb_frontend_parameters *params)
1327{
1328 struct drx397xD_state *s = fe->demodulator_priv;
1329
1330 s->config.s20d24 = 1; // 0;
1331 return drx_tune(s, params);
1332}
1333
1334static int drx397x_get_tune_settings(struct dvb_frontend *fe,
1335 struct dvb_frontend_tune_settings
1336 *fe_tune_settings)
1337{
1338 fe_tune_settings->min_delay_ms = 10000;
1339 fe_tune_settings->step_size = 0;
1340 fe_tune_settings->max_drift = 0;
1341 return 0;
1342}
1343
1344static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
1345{
1346 struct drx397xD_state *s = fe->demodulator_priv;
1347 int lockstat;
1348
1349 GetLockStatus(s, &lockstat);
1350 /* TODO */
1351// if (lockstat & 1)
1352// CorrectSysClockDeviation(s);
1353
1354 *status = 0;
1355 if (lockstat & 2) {
1356 CorrectSysClockDeviation(s);
1357 ConfigureMPEGOutput(s, 1);
1358 *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
1359 }
1360 if (lockstat & 4) {
1361 *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
1362 }
1363
1364 return 0;
1365}
1366
1367static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber)
1368{
1369 *ber = 0;
1370 return 0;
1371}
1372
1373static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr)
1374{
1375 *snr = 0;
1376 return 0;
1377}
1378
1379static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
1380{
1381 struct drx397xD_state *s = fe->demodulator_priv;
1382 int rc;
1383
1384 if (s->config.ifagc.d00 == 2) {
1385 *strength = 0xffff;
1386 return 0;
1387 }
1388 rc = RD16(s, 0x0c20035);
1389 if (rc < 0) {
1390 *strength = 0;
1391 return 0;
1392 }
1393 rc &= 0x3ff;
1394 /* Signal strength is calculated using the following formula:
1395 *
1396 * a = 2200 * 150 / (2200 + 150);
1397 * a = a * 3300 / (a + 820);
1398 * b = 2200 * 3300 / (2200 + 820);
1399 * c = (((b-a) * rc) >> 10 + a) << 4;
1400 * strength = ~c & 0xffff;
1401 *
1402 * The following does the same but with less rounding errors:
1403 */
1404 *strength = ~(7720 + (rc * 30744 >> 10));
1405 return 0;
1406}
1407
1408static int drx397x_read_ucblocks(struct dvb_frontend *fe,
1409 unsigned int *ucblocks)
1410{
1411 *ucblocks = 0;
1412 return 0;
1413}
1414
1415static int drx397x_sleep(struct dvb_frontend *fe)
1416{
1417 return 0;
1418}
1419
1420static void drx397x_release(struct dvb_frontend *fe)
1421{
1422 struct drx397xD_state *s = fe->demodulator_priv;
1423 printk(KERN_INFO "%s: release demodulator\n", mod_name);
1424 if (s) {
1425 drx_release_fw(s);
1426 kfree(s);
1427 }
1428
1429}
1430
1431static struct dvb_frontend_ops drx397x_ops = {
1432
1433 .info = {
1434 .name = "Micronas DRX397xD DVB-T Frontend",
1435 .type = FE_OFDM,
1436 .frequency_min = 47125000,
1437 .frequency_max = 855250000,
1438 .frequency_stepsize = 166667,
1439 .frequency_tolerance = 0,
1440 .caps = /* 0x0C01B2EAE */
1441 FE_CAN_FEC_1_2 | // = 0x2,
1442 FE_CAN_FEC_2_3 | // = 0x4,
1443 FE_CAN_FEC_3_4 | // = 0x8,
1444 FE_CAN_FEC_5_6 | // = 0x20,
1445 FE_CAN_FEC_7_8 | // = 0x80,
1446 FE_CAN_FEC_AUTO | // = 0x200,
1447 FE_CAN_QPSK | // = 0x400,
1448 FE_CAN_QAM_16 | // = 0x800,
1449 FE_CAN_QAM_64 | // = 0x2000,
1450 FE_CAN_QAM_AUTO | // = 0x10000,
1451 FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000,
1452 FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000,
1453 FE_CAN_HIERARCHY_AUTO | // = 0x100000,
1454 FE_CAN_RECOVER | // = 0x40000000,
1455 FE_CAN_MUTE_TS // = 0x80000000
1456 },
1457
1458 .release = drx397x_release,
1459 .init = drx397x_init,
1460 .sleep = drx397x_sleep,
1461
1462 .set_frontend = drx397x_set_frontend,
1463 .get_tune_settings = drx397x_get_tune_settings,
1464 .get_frontend = drx397x_get_frontend,
1465
1466 .read_status = drx397x_read_status,
1467 .read_snr = drx397x_read_snr,
1468 .read_signal_strength = drx397x_read_signal_strength,
1469 .read_ber = drx397x_read_ber,
1470 .read_ucblocks = drx397x_read_ucblocks,
1471};
1472
1473struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config,
1474 struct i2c_adapter *i2c)
1475{
1476 struct drx397xD_state *s = NULL;
1477
1478 /* allocate memory for the internal state */
1479 s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
1480 if (s == NULL)
1481 goto error;
1482
1483 /* setup the state */
1484 s->i2c = i2c;
1485 memcpy(&s->config, config, sizeof(struct drx397xD_config));
1486
1487 /* check if the demod is there */
1488 if (RD16(s, 0x2410019) < 0)
1489 goto error;
1490
1491 /* create dvb_frontend */
1492 memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops));
1493 s->frontend.demodulator_priv = s;
1494
1495 return &s->frontend;
1496 error:
1497 kfree(s);
1498 return NULL;
1499}
1500
1501MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend");
1502MODULE_AUTHOR("Henk Vergonet");
1503MODULE_LICENSE("GPL");
1504
1505EXPORT_SYMBOL(drx397xD_attach);