aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends/s5h1409.c
diff options
context:
space:
mode:
authorSteven Toth <stoth@hauppauge.com>2007-07-28 18:34:52 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-10-09 21:03:38 -0400
commit89885558ada9e076b48f4b6887e252e13e7eaf74 (patch)
tree606bd152aeb395649c8639b769cad0f42ea665a5 /drivers/media/dvb/frontends/s5h1409.c
parentf47623a04dab402fb2c18fe516a174bc02005629 (diff)
V4L/DVB (5948): Adding support for the S5H1409/CX24227 8VSB/QAM demodulator.
This patch adds support for the Samsung S5H1409 demodulator, also known as the Conexant CX24227 demodulator. 8VSB mode has been tested and QAM has been implemented based on the spec, although it's untested. The S5H1409 / CX24227 appears on various Hauppauge boards. Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/dvb/frontends/s5h1409.c')
-rw-r--r--drivers/media/dvb/frontends/s5h1409.c715
1 files changed, 715 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c
new file mode 100644
index 000000000000..4b77390f5d4b
--- /dev/null
+++ b/drivers/media/dvb/frontends/s5h1409.c
@@ -0,0 +1,715 @@
1/*
2 Samsung S5H1409 VSB/QAM demodulator driver
3
4 Copyright (C) 2006 Steven Toth <stoth@hauppauge.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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20*/
21
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/string.h>
26#include <linux/slab.h>
27#include <linux/delay.h>
28#include "dvb_frontend.h"
29#include "dvb-pll.h"
30#include "s5h1409.h"
31
32struct s5h1409_state {
33
34 struct i2c_adapter* i2c;
35
36 /* configuration settings */
37 const struct s5h1409_config* config;
38
39 struct dvb_frontend frontend;
40
41 /* previous uncorrected block counter */
42 fe_modulation_t current_modulation;
43
44 u32 current_frequency;
45};
46
47static int debug = 0;
48#define dprintk if (debug) printk
49
50/* Register values to initialise the demod, this will set VSB by default */
51static struct init_tab {
52 u8 reg;
53 u16 data;
54} init_tab[] = {
55 { 0x00, 0x0071, },
56 { 0x01, 0x3213, },
57 { 0x09, 0x0025, },
58 { 0x1c, 0x001d, },
59 { 0x1f, 0x002d, },
60 { 0x20, 0x001d, },
61 { 0x22, 0x0022, },
62 { 0x23, 0x0020, },
63 { 0x29, 0x110f, },
64 { 0x2a, 0x10b4, },
65 { 0x2b, 0x10ae, },
66 { 0x2c, 0x0031, },
67 { 0x31, 0x010d, },
68 { 0x32, 0x0100, },
69 { 0x44, 0x0510, },
70 { 0x54, 0x0104, },
71 { 0x58, 0x2222, },
72 { 0x59, 0x1162, },
73 { 0x5a, 0x3211, },
74 { 0x5d, 0x0370, },
75 { 0x5e, 0x0296, },
76 { 0x61, 0x0010, },
77 { 0x63, 0x4a00, },
78 { 0x65, 0x0800, },
79 { 0x71, 0x0003, },
80 { 0x72, 0x0470, },
81 { 0x81, 0x0002, },
82 { 0x82, 0x0600, },
83 { 0x86, 0x0002, },
84 { 0x8a, 0x2c38, },
85 { 0x8b, 0x2a37, },
86 { 0x92, 0x302f, },
87 { 0x93, 0x3332, },
88 { 0x96, 0x000c, },
89 { 0x99, 0x0101, },
90 { 0x9c, 0x2e37, },
91 { 0x9d, 0x2c37, },
92 { 0x9e, 0x2c37, },
93 { 0xab, 0x0100, },
94 { 0xac, 0x1003, },
95 { 0xad, 0x103f, },
96 { 0xe2, 0x0100, },
97 { 0x28, 0x1010, },
98 { 0xb1, 0x000e, },
99};
100
101/* VSB SNR lookup table */
102static struct vsb_snr_tab {
103 u16 val;
104 u16 data;
105} vsb_snr_tab[] = {
106 { 1023, 770, },
107 { 923, 300, },
108 { 918, 295, },
109 { 915, 290, },
110 { 911, 285, },
111 { 906, 280, },
112 { 901, 275, },
113 { 896, 270, },
114 { 891, 265, },
115 { 885, 260, },
116 { 879, 255, },
117 { 873, 250, },
118 { 864, 245, },
119 { 858, 240, },
120 { 850, 235, },
121 { 841, 230, },
122 { 832, 225, },
123 { 823, 220, },
124 { 812, 215, },
125 { 802, 210, },
126 { 788, 205, },
127 { 778, 200, },
128 { 767, 195, },
129 { 753, 190, },
130 { 740, 185, },
131 { 725, 180, },
132 { 707, 175, },
133 { 689, 170, },
134 { 671, 165, },
135 { 656, 160, },
136 { 637, 155, },
137 { 616, 150, },
138 { 542, 145, },
139 { 519, 140, },
140 { 507, 135, },
141 { 497, 130, },
142 { 492, 125, },
143 { 474, 120, },
144 { 300, 111, },
145 { 0, 0, },
146};
147
148/* QAM64 SNR lookup table */
149static struct qam64_snr_tab {
150 u16 val;
151 u16 data;
152} qam64_snr_tab[] = {
153 { 12, 300, },
154 { 15, 290, },
155 { 18, 280, },
156 { 22, 270, },
157 { 23, 268, },
158 { 24, 266, },
159 { 25, 264, },
160 { 27, 262, },
161 { 28, 260, },
162 { 29, 258, },
163 { 30, 256, },
164 { 32, 254, },
165 { 33, 252, },
166 { 34, 250, },
167 { 35, 249, },
168 { 36, 248, },
169 { 37, 247, },
170 { 38, 246, },
171 { 39, 245, },
172 { 40, 244, },
173 { 41, 243, },
174 { 42, 241, },
175 { 43, 240, },
176 { 44, 239, },
177 { 45, 238, },
178 { 46, 237, },
179 { 47, 236, },
180 { 48, 235, },
181 { 49, 234, },
182 { 50, 233, },
183 { 51, 232, },
184 { 52, 231, },
185 { 53, 230, },
186 { 55, 229, },
187 { 56, 228, },
188 { 57, 227, },
189 { 58, 226, },
190 { 59, 225, },
191 { 60, 224, },
192 { 62, 223, },
193 { 63, 222, },
194 { 65, 221, },
195 { 66, 220, },
196 { 68, 219, },
197 { 69, 218, },
198 { 70, 217, },
199 { 72, 216, },
200 { 73, 215, },
201 { 75, 214, },
202 { 76, 213, },
203 { 78, 212, },
204 { 80, 211, },
205 { 81, 210, },
206 { 83, 209, },
207 { 84, 208, },
208 { 85, 207, },
209 { 87, 206, },
210 { 89, 205, },
211 { 91, 204, },
212 { 93, 203, },
213 { 95, 202, },
214 { 96, 201, },
215 { 104, 200, },
216};
217
218/* QAM256 SNR lookup table */
219static struct qam256_snr_tab {
220 u16 val;
221 u16 data;
222} qam256_snr_tab[] = {
223 { 12, 400, },
224 { 13, 390, },
225 { 15, 380, },
226 { 17, 360, },
227 { 19, 350, },
228 { 22, 348, },
229 { 23, 346, },
230 { 24, 344, },
231 { 25, 342, },
232 { 26, 340, },
233 { 27, 336, },
234 { 28, 334, },
235 { 29, 332, },
236 { 30, 330, },
237 { 31, 328, },
238 { 32, 326, },
239 { 33, 325, },
240 { 34, 322, },
241 { 35, 320, },
242 { 37, 318, },
243 { 39, 316, },
244 { 40, 314, },
245 { 41, 312, },
246 { 42, 310, },
247 { 43, 308, },
248 { 46, 306, },
249 { 47, 304, },
250 { 49, 302, },
251 { 51, 300, },
252 { 53, 298, },
253 { 54, 297, },
254 { 55, 296, },
255 { 56, 295, },
256 { 57, 294, },
257 { 59, 293, },
258 { 60, 292, },
259 { 61, 291, },
260 { 63, 290, },
261 { 64, 289, },
262 { 65, 288, },
263 { 66, 287, },
264 { 68, 286, },
265 { 69, 285, },
266 { 71, 284, },
267 { 72, 283, },
268 { 74, 282, },
269 { 75, 281, },
270 { 76, 280, },
271 { 77, 279, },
272 { 78, 278, },
273 { 81, 277, },
274 { 83, 276, },
275 { 84, 275, },
276 { 86, 274, },
277 { 87, 273, },
278 { 89, 272, },
279 { 90, 271, },
280 { 92, 270, },
281 { 93, 269, },
282 { 95, 268, },
283 { 96, 267, },
284 { 98, 266, },
285 { 100, 265, },
286 { 102, 264, },
287 { 104, 263, },
288 { 105, 262, },
289 { 106, 261, },
290 { 110, 260, },
291};
292
293/* 8 bit registers, 16 bit values */
294static int s5h1409_writereg(struct s5h1409_state* state, u8 reg, u16 data)
295{
296 int ret;
297 u8 buf [] = { reg, data >> 8, data & 0xff };
298
299 struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 };
300
301 ret = i2c_transfer(state->i2c, &msg, 1);
302
303 if (ret != 1)
304 printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
305 __FUNCTION__, reg, data, ret);
306
307 return (ret != 1) ? -1 : 0;
308}
309
310static u16 s5h1409_readreg(struct s5h1409_state* state, u8 reg)
311{
312 int ret;
313 u8 b0 [] = { reg };
314 u8 b1 [] = { 0, 0 };
315
316 struct i2c_msg msg [] = {
317 { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
318 { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
319
320 ret = i2c_transfer(state->i2c, msg, 2);
321
322 if (ret != 2)
323 printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret)
324 ;
325 return (b1[0] << 8) | b1[1];
326}
327
328static int s5h1409_softreset(struct dvb_frontend* fe)
329{
330 struct s5h1409_state* state = fe->demodulator_priv;
331
332 dprintk("%s()\n", __FUNCTION__);
333
334 s5h1409_writereg(state, 0xf5, 0);
335 s5h1409_writereg(state, 0xf5, 1);
336 return 0;
337}
338
339static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz)
340{
341 struct s5h1409_state* state = fe->demodulator_priv;
342 int ret = 0;
343
344 dprintk("%s(%d KHz)\n", __FUNCTION__, KHz);
345
346 if( (KHz == 44000) || (KHz == 5380) )
347 {
348 s5h1409_writereg(state, 0x87, 0x01be);
349 s5h1409_writereg(state, 0x88, 0x0436);
350 s5h1409_writereg(state, 0x89, 0x054d);
351 } else {
352 printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz);
353 ret = -1;
354 }
355
356 return ret;
357}
358
359static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted)
360{
361 struct s5h1409_state* state = fe->demodulator_priv;
362
363 dprintk("%s()\n", __FUNCTION__);
364
365 if(inverted == 1)
366 return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */
367 else
368 return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */
369}
370
371static int s5h1409_enable_modulation(struct dvb_frontend* fe, fe_modulation_t m)
372{
373 struct s5h1409_state* state = fe->demodulator_priv;
374
375 dprintk("%s(0x%08x)\n", __FUNCTION__, m);
376
377 switch(m) {
378 case VSB_8:
379 dprintk("%s() VSB_8\n", __FUNCTION__);
380 s5h1409_writereg(state, 0xf4, 0);
381 break;
382 case QAM_64:
383 dprintk("%s() QAM_64\n", __FUNCTION__);
384 s5h1409_writereg(state, 0xf4, 1);
385 s5h1409_writereg(state, 0x85, 0x100);
386 break;
387 case QAM_256:
388 dprintk("%s() QAM_256\n", __FUNCTION__);
389 s5h1409_writereg(state, 0xf4, 1);
390 s5h1409_writereg(state, 0x85, 0x101);
391 break;
392 default:
393 dprintk("%s() Invalid modulation\n", __FUNCTION__);
394 return -EINVAL;
395 }
396
397 state->current_modulation = m;
398 s5h1409_softreset(fe);
399
400 return 0;
401}
402
403static int s5h1409_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
404{
405 struct s5h1409_state* state = fe->demodulator_priv;
406
407 dprintk("%s(%d)\n", __FUNCTION__, enable);
408
409 if (enable)
410 return s5h1409_writereg(state, 0xf3, 1);
411 else
412 return s5h1409_writereg(state, 0xf3, 0);
413}
414
415static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable)
416{
417 struct s5h1409_state* state = fe->demodulator_priv;
418
419 dprintk("%s(%d)\n", __FUNCTION__, enable);
420
421 if (enable)
422 return s5h1409_writereg(state, 0xe3, 0x1100);
423 else
424 return s5h1409_writereg(state, 0xe3, 0);
425}
426
427static int s5h1409_sleep(struct dvb_frontend* fe, int enable)
428{
429 struct s5h1409_state* state = fe->demodulator_priv;
430
431 dprintk("%s(%d)\n", __FUNCTION__, enable);
432
433 return s5h1409_writereg(state, 0xf2, enable);
434}
435
436static int s5h1409_register_reset(struct dvb_frontend* fe)
437{
438 struct s5h1409_state* state = fe->demodulator_priv;
439
440 dprintk("%s()\n", __FUNCTION__);
441
442 return s5h1409_writereg(state, 0xfa, 0);
443}
444
445/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
446static int s5h1409_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
447{
448 struct s5h1409_state* state = fe->demodulator_priv;
449
450 dprintk("%s(frequency=%d)\n", __FUNCTION__, p->frequency);
451
452 s5h1409_softreset(fe);
453
454 state->current_frequency = p->frequency;
455
456 s5h1409_enable_modulation(fe, p->u.vsb.modulation);
457
458 if (fe->ops.tuner_ops.set_params) {
459 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
460 fe->ops.tuner_ops.set_params(fe, p);
461 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
462 }
463
464 return 0;
465}
466
467/* Reset the demod hardware and reset all of the configuration registers
468 to a default state. */
469static int s5h1409_init (struct dvb_frontend* fe)
470{
471 int i;
472
473 struct s5h1409_state* state = fe->demodulator_priv;
474 dprintk("%s()\n", __FUNCTION__);
475
476 s5h1409_sleep(fe, 0);
477 s5h1409_register_reset(fe);
478
479 for (i=0; i<sizeof(init_tab) / sizeof (struct init_tab); i++)
480 s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data);
481
482 /* The datasheet says that after initialisation, VSB is default */
483 state->current_modulation = VSB_8;
484
485 if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
486 s5h1409_writereg(state, 0xab, 0x100); /* Serial */
487 else
488 s5h1409_writereg(state, 0xab, 0x0); /* Parallel */
489
490 s5h1409_set_spectralinversion(fe, state->config->inversion);
491 s5h1409_set_if_freq(fe, state->config->if_freq);
492 s5h1409_set_gpio(fe, state->config->gpio);
493 s5h1409_softreset(fe);
494
495 /* Note: Leaving the I2C gate open here. */
496 s5h1409_i2c_gate_ctrl(fe, 1);
497
498 return 0;
499}
500
501static int s5h1409_read_status(struct dvb_frontend* fe, fe_status_t* status)
502{
503 struct s5h1409_state* state = fe->demodulator_priv;
504 u16 reg;
505 u32 tuner_status = 0;
506
507 *status = 0;
508
509 /* Get the demodulator status */
510 reg = s5h1409_readreg(state, 0xf1);
511 if(reg & 0x1000)
512 *status |= FE_HAS_VITERBI;
513 if(reg & 0x8000)
514 *status |= FE_HAS_LOCK | FE_HAS_SYNC;
515
516 switch(state->config->status_mode) {
517 case S5H1409_DEMODLOCKING:
518 if (*status & FE_HAS_VITERBI)
519 *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
520 break;
521 case S5H1409_TUNERLOCKING:
522 /* Get the tuner status */
523 if (fe->ops.tuner_ops.get_status) {
524 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
525
526 fe->ops.tuner_ops.get_status(fe, &tuner_status);
527
528 if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
529 }
530 if (tuner_status)
531 *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
532 break;
533 }
534
535 dprintk("%s() status 0x%08x\n", __FUNCTION__, *status);
536
537 return 0;
538}
539
540static int s5h1409_qam256_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v)
541{
542 int i, ret = -EINVAL;
543 dprintk("%s()\n", __FUNCTION__);
544
545 for (i=0; i<sizeof(qam256_snr_tab) / (sizeof(struct qam256_snr_tab)); i++) {
546 if (v < qam256_snr_tab[i].val) {
547 *snr = qam256_snr_tab[i].data;
548 ret = 0;
549 break;
550 }
551 }
552 return ret;
553}
554
555static int s5h1409_qam64_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v)
556{
557 int i, ret = -EINVAL;
558 dprintk("%s()\n", __FUNCTION__);
559
560 for (i=0; i<sizeof(qam64_snr_tab) / (sizeof(struct qam64_snr_tab)); i++) {
561 if (v < qam64_snr_tab[i].val) {
562 *snr = qam64_snr_tab[i].data;
563 ret = 0;
564 break;
565 }
566 }
567 return ret;
568}
569
570static int s5h1409_vsb_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v)
571{
572 int i, ret = -EINVAL;
573 dprintk("%s()\n", __FUNCTION__);
574
575 for (i=0; i<sizeof(vsb_snr_tab) / (sizeof(struct vsb_snr_tab)); i++) {
576 if (v > vsb_snr_tab[i].val) {
577 *snr = vsb_snr_tab[i].data;
578 ret = 0;
579 break;
580 }
581 }
582 dprintk("%s() snr=%d\n", __FUNCTION__, *snr);
583 return ret;
584}
585
586static int s5h1409_read_snr(struct dvb_frontend* fe, u16* snr)
587{
588 struct s5h1409_state* state = fe->demodulator_priv;
589 u16 reg;
590 dprintk("%s()\n", __FUNCTION__);
591
592 reg = s5h1409_readreg(state, 0xf1) & 0x1ff;
593
594 switch(state->current_modulation) {
595 case QAM_64:
596 return s5h1409_qam64_lookup_snr(fe, snr, reg);
597 case QAM_256:
598 return s5h1409_qam256_lookup_snr(fe, snr, reg);
599 case VSB_8:
600 return s5h1409_vsb_lookup_snr(fe, snr, reg);
601 default:
602 break;
603 }
604
605 return -EINVAL;
606}
607
608static int s5h1409_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
609{
610 return s5h1409_read_snr(fe, signal_strength);
611}
612
613static int s5h1409_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
614{
615 struct s5h1409_state* state = fe->demodulator_priv;
616
617 *ucblocks = s5h1409_readreg(state, 0xb5);
618
619 return 0;
620}
621
622static int s5h1409_read_ber(struct dvb_frontend* fe, u32* ber)
623{
624 return s5h1409_read_ucblocks(fe, ber);
625}
626
627static int s5h1409_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
628{
629 struct s5h1409_state* state = fe->demodulator_priv;
630
631 p->frequency = state->current_frequency;
632 p->u.vsb.modulation = state->current_modulation;
633
634 return 0;
635}
636
637static int s5h1409_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
638{
639 tune->min_delay_ms = 1000;
640 return 0;
641}
642
643static void s5h1409_release(struct dvb_frontend* fe)
644{
645 struct s5h1409_state* state = fe->demodulator_priv;
646 kfree(state);
647}
648
649static struct dvb_frontend_ops s5h1409_ops;
650
651struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
652 struct i2c_adapter* i2c)
653{
654 struct s5h1409_state* state = NULL;
655
656 /* allocate memory for the internal state */
657 state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
658 if (state == NULL)
659 goto error;
660
661 /* setup the state */
662 state->config = config;
663 state->i2c = i2c;
664 state->current_modulation = 0;
665
666 /* check if the demod exists */
667 if (s5h1409_readreg(state, 0x04) != 0x0066)
668 goto error;
669
670 /* create dvb_frontend */
671 memcpy(&state->frontend.ops, &s5h1409_ops, sizeof(struct dvb_frontend_ops));
672 state->frontend.demodulator_priv = state;
673
674 /* Note: Leaving the I2C gate open here. */
675 s5h1409_writereg(state, 0xf3, 1);
676
677 return &state->frontend;
678
679error:
680 kfree(state);
681 return NULL;
682}
683
684static struct dvb_frontend_ops s5h1409_ops = {
685
686 .info = {
687 .name = "Samsung S5H1409 QAM/8VSB Frontend",
688 .type = FE_ATSC,
689 .frequency_min = 54000000,
690 .frequency_max = 858000000,
691 .frequency_stepsize = 62500,
692 .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
693 },
694
695 .init = s5h1409_init,
696 .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl,
697 .set_frontend = s5h1409_set_frontend,
698 .get_frontend = s5h1409_get_frontend,
699 .get_tune_settings = s5h1409_get_tune_settings,
700 .read_status = s5h1409_read_status,
701 .read_ber = s5h1409_read_ber,
702 .read_signal_strength = s5h1409_read_signal_strength,
703 .read_snr = s5h1409_read_snr,
704 .read_ucblocks = s5h1409_read_ucblocks,
705 .release = s5h1409_release,
706};
707
708module_param(debug, int, 0644);
709MODULE_PARM_DESC(debug, "Enable verbose debug messages");
710
711MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");
712MODULE_AUTHOR("Steven Toth");
713MODULE_LICENSE("GPL");
714
715EXPORT_SYMBOL(s5h1409_attach);