tuner-xc2028.c 16.2 KB
Newer Older
1 2 3
/* tuner-xc2028
 *
 * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
4
 *
5 6
 * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
 *       - frontend interface
7
 *
8 9 10 11 12 13
 * This code is placed under the terms of the GNU General Public License v2
 */

#include <linux/i2c.h>
#include <asm/div64.h>
#include <linux/firmware.h>
14
#include <linux/videodev2.h>
15
#include <linux/delay.h>
16
#include <media/tuner.h>
17
#include <linux/mutex.h>
18
#include "tuner-i2c.h"
19
#include "tuner-xc2028.h"
20
#include "tuner-xc2028-types.h"
21

22 23 24
#include <linux/dvb/frontend.h>
#include "dvb_frontend.h"

25 26 27
#define PREFIX "xc2028 "

static LIST_HEAD(xc2028_list);
28 29 30 31 32 33 34
/* struct for storing firmware table */
struct firmware_description {
	unsigned int  type;
	v4l2_std_id   id;
	unsigned char *ptr;
	unsigned int  size;
};
35 36

struct xc2028_data {
37 38 39 40 41 42 43
	struct list_head        xc2028_list;
	struct tuner_i2c_props  i2c_props;
	int                     (*tuner_callback) (void *dev,
						   int command, int arg);
	struct device           *dev;
	void			*video_dev;
	int			count;
44 45 46 47 48 49 50 51
	__u32			frequency;

	struct firmware_description *firm;
	int			firm_size;

	__u16			version;

	struct xc2028_ctrl	ctrl;
52

53 54 55 56 57 58
	v4l2_std_id		firm_type;	   /* video stds supported
							by current firmware */
	fe_bandwidth_t		bandwidth;	   /* Firmware bandwidth:
							      6M, 7M or 8M */
	int			need_load_generic; /* The generic firmware
							      were loaded? */
59 60 61

	int			max_len;	/* Max firmware chunk */

62 63
	enum tuner_mode	mode;
	struct i2c_client	*i2c_client;
64 65

	struct mutex lock;
66 67
};

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
#define i2c_send(rc, priv, buf, size) do {				\
	rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size);		\
	if (size != rc)							\
		tuner_info("i2c output error: rc = %d (should be %d)\n",\
			   rc, (int)size);				\
} while (0)

#define i2c_rcv(rc, priv, buf, size) do {				\
	rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size);		\
	if (size != rc)							\
		tuner_info("i2c input error: rc = %d (should be %d)\n",	\
			   rc, (int)size); 				\
} while (0)

#define send_seq(priv, data...)	do {					\
	int rc;								\
84
	static u8 _val[] = data;					\
85
	if (sizeof(_val) !=						\
86
			(rc = tuner_i2c_xfer_send(&priv->i2c_props,	\
87
						_val, sizeof(_val)))) {	\
88 89
		tuner_info("Error on line %d: %d\n", __LINE__, rc);	\
		return -EINVAL;						\
90
	}								\
91 92
	msleep(10);							\
} while (0)
93

94
static int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
95 96 97
{
	int rc;
	unsigned char buf[1];
98 99

	tuner_info("%s called\n", __FUNCTION__);
100

101
	buf[0] = reg;
102

103
	i2c_send(rc, priv, buf, sizeof(buf));
104
	if (rc < 0)
105 106
		return rc;

107
	i2c_rcv(rc, priv, buf, 2);
108
	if (rc < 0)
109 110
		return rc;

111
	return (buf[1]) | (buf[0] << 8);
112 113
}

114
static void free_firmware(struct xc2028_data *priv)
115
{
116 117 118 119 120
	int i;

	if (!priv->firm)
		return;

121 122 123
	for (i = 0; i < priv->firm_size; i++)
		kfree(priv->firm[i].ptr);

124 125
	kfree(priv->firm);

126
	priv->firm = NULL;
127 128 129
	priv->need_load_generic = 1;
}

130
static int load_all_firmwares(struct dvb_frontend *fe)
131 132
{
	struct xc2028_data    *priv = fe->tuner_priv;
133
	const struct firmware *fw   = NULL;
134
	unsigned char         *p, *endp;
135 136
	int                   rc = 0;
	int		      n, n_array;
137
	char		      name[33];
138

139 140
	tuner_info("%s called\n", __FUNCTION__);

141 142
	tuner_info("Loading firmware %s\n", priv->ctrl.fname);
	rc = request_firmware(&fw, priv->ctrl.fname, priv->dev);
143
	if (rc < 0) {
144
		if (rc == -ENOENT)
145 146
			tuner_info("Error: firmware %s not found.\n",
				   priv->ctrl.fname);
147
		else
148 149
			tuner_info("Error %d while requesting firmware %s \n",
				   rc, priv->ctrl.fname);
150

151 152
		return rc;
	}
153 154
	p = fw->data;
	endp = p + fw->size;
155

156
	if (fw->size < sizeof(name) - 1 + 2) {
157
		tuner_info("Error: firmware size is zero!\n");
158
		rc = -EINVAL;
159
		goto done;
160
	}
161

162 163 164
	memcpy(name, p, sizeof(name) - 1);
	name[sizeof(name) - 1] = 0;
	p += sizeof(name) - 1;
165

166
	priv->version = le16_to_cpu(*(__u16 *) p);
167 168 169
	p += 2;

	tuner_info("firmware: %s, ver %d.%d\n", name,
170
		   priv->version >> 8, priv->version & 0xff);
171

172
	if (p + 2 > endp)
173 174
		goto corrupt;

175
	n_array = le16_to_cpu(*(__u16 *) p);
176 177 178 179
	p += 2;

	tuner_info("there are %d firmwares at %s\n", n_array, priv->ctrl.fname);

180
	priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
181 182 183

	if (!fw) {
		tuner_info("Not enough memory for loading firmware.\n");
184
		rc = -ENOMEM;
185
		goto done;
186 187
	}

188
	priv->firm_size = n_array;
189 190
	n = -1;
	while (p < endp) {
191 192 193 194 195 196 197 198 199 200
		__u32 type, size;
		v4l2_std_id id;

		n++;
		if (n >= n_array) {
			tuner_info("Too much firmwares at the file\n");
			goto corrupt;
		}

		/* Checks if there's enough bytes to read */
201
		if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
202 203 204 205
			tuner_info("Lost firmware!\n");
			goto corrupt;
		}

206
		type = le32_to_cpu(*(__u32 *) p);
207 208
		p += sizeof(type);

209
		id = le64_to_cpu(*(v4l2_std_id *) p);
210 211
		p += sizeof(id);

212
		size = le32_to_cpu(*(v4l2_std_id *) p);
213 214
		p += sizeof(size);

215
		if ((!size) || (size + p > endp)) {
216
			tuner_info("Firmware type %x, id %lx corrupt\n",
217
				   type, (unsigned long)id);
218 219 220
			goto corrupt;
		}

221
		priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
222 223
		if (!priv->firm[n].ptr) {
			tuner_info("Not enough memory.\n");
224
			rc = -ENOMEM;
225 226 227
			goto err;
		}
		tuner_info("Loading firmware type %x, id %lx, size=%d.\n",
228
			   type, (unsigned long)id, size);
229 230 231 232 233 234 235 236 237

		memcpy(priv->firm[n].ptr, p, size);
		priv->firm[n].type = type;
		priv->firm[n].id   = id;
		priv->firm[n].size = size;

		p += size;
	}

238
	if (n + 1 != priv->firm_size) {
239 240 241 242 243 244 245
		tuner_info("Firmware file is incomplete!\n");
		goto corrupt;
	}

	goto done;

corrupt:
246
	rc = -EINVAL;
247 248 249 250 251 252 253 254 255 256 257 258 259 260
	tuner_info("Error: firmware file is corrupted!\n");

err:
	tuner_info("Releasing loaded firmware file.\n");

	free_firmware(priv);

done:
	release_firmware(fw);
	tuner_info("Firmware files loaded.\n");

	return rc;
}

261 262
static int load_firmware(struct dvb_frontend *fe, unsigned int type,
			 v4l2_std_id * id)
263 264
{
	struct xc2028_data *priv = fe->tuner_priv;
265 266
	int                i, rc;
	unsigned char      *p, *endp, buf[priv->max_len];
267 268 269 270

	tuner_info("%s called\n", __FUNCTION__);

	if (!priv->firm) {
271
		printk(KERN_ERR PREFIX "Error! firmware not loaded\n");
272 273 274 275
		return -EINVAL;
	}

	if ((type == 0) && (*id == 0))
276
		*id = V4L2_STD_PAL;
277 278

	/* Seek for exact match */
279 280
	for (i = 0; i < priv->firm_size; i++) {
		if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
281 282 283 284
			goto found;
	}

	/* Seek for generic video standard match */
285 286
	for (i = 0; i < priv->firm_size; i++) {
		if ((type == priv->firm[i].type) && (*id & priv->firm[i].id))
287 288 289 290 291
			goto found;
	}

	/*FIXME: Would make sense to seek for type "hint" match ? */

292 293
	tuner_info("Can't find firmware for type=%x, id=%lx\n", type,
		   (long int)*id);
294 295 296 297
	return -EINVAL;

found:
	*id = priv->firm[i].id;
298
	tuner_info("Found firmware for type=%x, id=%lx\n", type, (long int)*id);
299 300 301 302 303 304

	p = priv->firm[i].ptr;

	if (!p) {
		printk(KERN_ERR PREFIX "Firmware pointer were freed!");
		return -EINVAL;
305
	}
306
	endp = p + priv->firm[i].size;
307

308
	while (p < endp) {
309 310 311
		__u16 size;

		/* Checks if there's enough bytes to read */
312
		if (p + sizeof(size) > endp) {
313 314 315 316
			tuner_info("missing bytes\n");
			return -EINVAL;
		}

317
		size = le16_to_cpu(*(__u16 *) p);
318 319 320 321 322 323
		p += sizeof(size);

		if (size == 0xffff)
			return 0;

		if (!size) {
324
			/* Special callback command received */
325
			rc = priv->tuner_callback(priv->video_dev,
326 327
						  XC2028_TUNER_RESET, 0);
			if (rc < 0) {
328
				tuner_info("Error at RESET code %d\n",
329
					   (*p) & 0x7f);
330
				return -EINVAL;
331 332 333
			}
			continue;
		}
334 335 336

		/* Checks for a sleep command */
		if (size & 0x8000) {
337
			msleep(size & 0x7fff);
338
			continue;
339 340
		}

341 342
		if ((size + p > endp)) {
			tuner_info("missing bytes: need %d, have %d\n",
343
				   size, (int)(endp - p));
344 345
			return -EINVAL;
		}
346

347
		buf[0] = *p;
348
		p++;
349
		size--;
350

351
		/* Sends message chunks */
352 353 354
		while (size > 0) {
			int len = (size < priv->max_len - 1) ?
				   size : priv->max_len - 1;
355

356
			memcpy(buf + 1, p, len);
357

358 359 360
			i2c_send(rc, priv, buf, len + 1);
			if (rc < 0) {
				tuner_info("%d returned from send\n", rc);
361 362 363 364 365 366 367 368
				return -EINVAL;
			}

			p += len;
			size -= len;
		}
	}
	return -EINVAL;
369 370
}

371
static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
372
			  v4l2_std_id std, fe_bandwidth_t bandwidth)
373
{
374
	struct xc2028_data      *priv = fe->tuner_priv;
375
	int			rc, version;
376 377
	v4l2_std_id		std0 = 0;
	unsigned int		type0 = 0, type = 0;
378
	int			change_digital_bandwidth;
379

380
	tuner_info("%s called\n", __FUNCTION__);
381

382 383 384 385
	if (!priv->firm) {
		if (!priv->ctrl.fname)
			return -EINVAL;

386 387
		rc = load_all_firmwares(fe);
		if (rc < 0)
388 389 390
			return rc;
	}

391 392
	tuner_info("I am in mode %u and I should switch to mode %i\n",
		   priv->mode, new_mode);
393 394

	/* first of all, determine whether we have switched the mode */
395
	if (new_mode != priv->mode) {
396 397
		priv->mode = new_mode;
		priv->need_load_generic = 1;
398 399
	}

400
	change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
401
				    && bandwidth != priv->bandwidth) ? 1 : 0;
402
	tuner_info("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
403
		   bandwidth);
404

405
	if (priv->need_load_generic) {
406
		/* Reset is needed before loading firmware */
407 408
		rc = priv->tuner_callback(priv->video_dev,
					  XC2028_TUNER_RESET, 0);
409
		if (rc < 0)
410 411
			return rc;

412
		type0 = BASE;
413 414 415 416

		if (priv->ctrl.type == XC2028_FIRM_MTS)
			type0 |= MTS;

417
		if (priv->bandwidth == 8)
418 419 420 421 422
			type0 |= F8MHZ;

		/* FIXME: How to load FM and FM|INPUT1 firmwares? */

		rc = load_firmware(fe, type0, &std0);
423
		if (rc < 0) {
424 425
			tuner_info("Error %d while loading generic firmware\n",
				   rc);
426
			return rc;
427
		}
428

429 430 431 432
		priv->need_load_generic = 0;
		priv->firm_type = 0;
		if (priv->mode == T_DIGITAL_TV)
			change_digital_bandwidth = 1;
433 434
	}

435
	tuner_info("I should change bandwidth %u\n", change_digital_bandwidth);
436 437

	if (change_digital_bandwidth) {
438 439 440 441 442 443

		/*FIXME: Should allow selecting between D2620 and D2633 */
		type |= D2620;

		/* FIXME: When should select a DTV78 firmware?
		 */
444
		switch (bandwidth) {
445 446
		case BANDWIDTH_8_MHZ:
			type |= DTV8;
447
			break;
448 449
		case BANDWIDTH_7_MHZ:
			type |= DTV7;
450
			break;
451 452 453
		case BANDWIDTH_6_MHZ:
			/* FIXME: Should allow select also ATSC */
			type |= DTV6_QAM;
454 455
			break;

456 457
		default:
			tuner_info("error: bandwidth not supported.\n");
458
		};
459
		priv->bandwidth = bandwidth;
460 461
	}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
	/* Load INIT1, if needed */
	tuner_info("Trying to load init1 firmware\n");
	type0 = BASE | INIT1 | priv->ctrl.type;
	if (priv->ctrl.type == XC2028_FIRM_MTS)
		type0 |= MTS;

	/* FIXME: Should handle errors - if INIT1 found */
	rc = load_firmware(fe, type0, &std0);

	/* FIXME: Should add support for FM radio
	 */

	if (priv->ctrl.type == XC2028_FIRM_MTS)
		type |= MTS;

477
	tuner_info("firmware standard to load: %08lx\n", (unsigned long)std);
478
	if (priv->firm_type & std) {
479
		tuner_info("no need to load a std-specific firmware.\n");
480
		return 0;
481
	}
482

483
	rc = load_firmware(fe, type, &std);
484
	if (rc < 0)
485 486
		return rc;

487
	version = xc2028_get_reg(priv, 0x4);
488
	tuner_info("Firmware version is %d.%d\n",
489
		   (version >> 4) & 0x0f, (version) & 0x0f);
490

491
	priv->firm_type = std;
492 493 494 495

	return 0;
}

496
static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
497
{
498
	struct xc2028_data *priv = fe->tuner_priv;
499
	int                frq_lock, signal = 0;
500

501
	tuner_info("%s called\n", __FUNCTION__);
502

503
	mutex_lock(&priv->lock);
504

505 506 507
	*strength = 0;

	frq_lock = xc2028_get_reg(priv, 0x2);
508
	if (frq_lock <= 0)
509
		goto ret;
510 511 512

	/* Frequency is locked. Return signal quality */

513
	signal = xc2028_get_reg(priv, 0x40);
514

515 516
	if (signal <= 0)
		signal = frq_lock;
517 518

ret:
519 520 521
	mutex_unlock(&priv->lock);

	*strength = signal;
522

523
	return 0;
524 525 526 527
}

#define DIV 15625

528 529 530
static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
			       enum tuner_mode new_mode,
			       v4l2_std_id std, fe_bandwidth_t bandwidth)
531
{
532
	struct xc2028_data *priv = fe->tuner_priv;
533 534 535
	int		   rc = -EINVAL;
	unsigned char	   buf[5];
	u32		   div, offset = 0;
536

537 538
	tuner_info("%s called\n", __FUNCTION__);

539 540
	mutex_lock(&priv->lock);

541 542
	/* HACK: It seems that specific firmware need to be reloaded
	   when freq is changed */
543

544
	priv->firm_type = 0;
545

546
	/* Reset GPIO 1 */
547
	rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
548
	if (rc < 0)
549 550
		goto ret;

551
	msleep(10);
552
	tuner_info("should set frequency %d kHz)\n", freq / 1000);
553

554
	if (check_firmware(fe, new_mode, std, bandwidth) < 0)
555
		goto ret;
556

557
	if (new_mode == T_DIGITAL_TV)
558
		offset = 2750000;
559

560
	div = (freq - offset + DIV / 2) / DIV;
561

562
	/* CMD= Set frequency */
563

564
	if (priv->version < 0x0202) {
565 566 567 568 569
		send_seq(priv, {0x00, 0x02, 0x00, 0x00});
	} else {
		send_seq(priv, {0x80, 0x02, 0x00, 0x00});
	}

570
	rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
571
	if (rc < 0)
572
		goto ret;
573 574

	msleep(10);
575

576 577 578 579 580
	buf[0] = 0xff & (div >> 24);
	buf[1] = 0xff & (div >> 16);
	buf[2] = 0xff & (div >> 8);
	buf[3] = 0xff & (div);
	buf[4] = 0;
581

582
	i2c_send(rc, priv, buf, sizeof(buf));
583
	if (rc < 0)
584
		goto ret;
585 586
	msleep(100);

587
	priv->frequency = freq;
588

589
	printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
590 591
	       buf[1], buf[2], buf[3], buf[4],
	       freq / 1000000, (freq % 1000000) / 10000);
592

593
	rc = 0;
594

595 596
ret:
	mutex_unlock(&priv->lock);
597

598
	return rc;
599 600
}

601
static int xc2028_set_tv_freq(struct dvb_frontend *fe,
602
			      struct analog_parameters *p)
603
{
604
	struct xc2028_data *priv = fe->tuner_priv;
605

606
	tuner_info("%s called\n", __FUNCTION__);
607

608 609
	return generic_set_tv_freq(fe, 62500l * p->frequency, T_ANALOG_TV,
				   p->std, BANDWIDTH_8_MHZ /* NOT USED */);
610
}
611

612 613
static int xc2028_set_params(struct dvb_frontend *fe,
			     struct dvb_frontend_parameters *p)
614
{
615
	struct xc2028_data *priv = fe->tuner_priv;
616

617
	tuner_info("%s called\n", __FUNCTION__);
618

619 620
	/* FIXME: Only OFDM implemented */
	if (fe->ops.info.type != FE_OFDM) {
621
		tuner_info("DTV type not implemented.\n");
622
		return -EINVAL;
623 624
	}

625
	return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
626 627
				   0 /* NOT USED */,
				   p->u.ofdm.bandwidth);
628 629

}
630

631
static int xc2028_dvb_release(struct dvb_frontend *fe)
632
{
633 634 635
	struct xc2028_data *priv = fe->tuner_priv;

	tuner_info("%s called\n", __FUNCTION__);
636

637
	priv->count--;
638

639
	if (!priv->count) {
640 641
		list_del(&priv->xc2028_list);

642
		kfree(priv->ctrl.fname);
643 644

		free_firmware(priv);
645
		kfree(priv);
646
	}
647 648 649 650

	return 0;
}

651
static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
652
{
653
	struct xc2028_data *priv = fe->tuner_priv;
654

655
	tuner_info("%s called\n", __FUNCTION__);
656

657
	*frequency = priv->frequency;
658 659 660 661

	return 0;
}

662
static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
663 664 665 666 667 668 669 670 671
{
	struct xc2028_data *priv = fe->tuner_priv;
	struct xc2028_ctrl *p    = priv_cfg;

	tuner_info("%s called\n", __FUNCTION__);

	priv->ctrl.type = p->type;

	if (p->fname) {
672
		kfree(priv->ctrl.fname);
673

674
		priv->ctrl.fname = kmalloc(strlen(p->fname) + 1, GFP_KERNEL);
675 676 677 678 679 680 681
		if (!priv->ctrl.fname)
			return -ENOMEM;

		free_firmware(priv);
		strcpy(priv->ctrl.fname, p->fname);
	}

682
	if (p->max_len > 0)
683 684
		priv->max_len = p->max_len;

685 686 687 688 689
	tuner_info("%s OK\n", __FUNCTION__);

	return 0;
}

690
static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
691
	.info = {
692 693 694 695 696
		 .name = "Xceive XC3028",
		 .frequency_min = 42000000,
		 .frequency_max = 864000000,
		 .frequency_step = 50000,
		 },
697

698
	.set_config	   = xc2028_set_config,
699 700 701 702 703
	.set_analog_params = xc2028_set_tv_freq,
	.release           = xc2028_dvb_release,
	.get_frequency     = xc2028_get_frequency,
	.get_rf_strength   = xc2028_signal,
	.set_params        = xc2028_set_params,
704 705 706

};

707
int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap,
708
		  u8 i2c_addr, struct device *dev, void *video_dev,
709
		  int (*tuner_callback) (void *dev, int command, int arg))
710
{
711
	struct xc2028_data *priv;
712

713
	printk(KERN_INFO PREFIX "Xcv2028/3028 init called!\n");
714

715 716 717 718 719 720 721
	if (NULL == dev)
		return -ENODEV;

	if (NULL == video_dev)
		return -ENODEV;

	if (!tuner_callback) {
722
		printk(KERN_ERR PREFIX "No tuner callback!\n");
723 724 725 726
		return -EINVAL;
	}

	list_for_each_entry(priv, &xc2028_list, xc2028_list) {
727
		if (priv->dev == dev)
728 729 730 731 732 733 734
			dev = NULL;
	}

	if (dev) {
		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
		if (priv == NULL)
			return -ENOMEM;
735

736
		fe->tuner_priv = priv;
737

738 739
		priv->bandwidth = BANDWIDTH_6_MHZ;
		priv->need_load_generic = 1;
740 741 742 743 744 745
		priv->mode = T_UNINITIALIZED;
		priv->i2c_props.addr = i2c_addr;
		priv->i2c_props.adap = i2c_adap;
		priv->dev = dev;
		priv->video_dev = video_dev;
		priv->tuner_callback = tuner_callback;
746 747
		priv->max_len = 13;

748 749 750

		mutex_init(&priv->lock);

751
		list_add_tail(&priv->xc2028_list, &xc2028_list);
752
	}
753
	priv->count++;
754 755

	memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
756
	       sizeof(xc2028_dvb_tuner_ops));
757 758 759 760 761

	tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");

	return 0;
}
762 763
EXPORT_SYMBOL(xc2028_attach);

764
MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
765
MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
766 767
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");