blob: ba2c5c91da09d82a7cfd20aefa2ed8c2fe2b5407 [file] [log] [blame]
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -03001/* DVB USB compliant linux driver for MSI Mega Sky 580 DVB-T USB2.0 receiver
2 *
3 * Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org)
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation, version 2.
8 *
9 * see Documentation/dvb/README.dvb-usb for more information
10 */
Michael Krufkybaa2ed02006-09-23 20:01:29 -030011
Michael Krufky2aef7d02006-09-23 20:00:42 -030012#include "m920x.h"
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -030013
14#include "mt352.h"
15#include "mt352_priv.h"
16
17/* debug */
Michael Krufkybaa2ed02006-09-23 20:01:29 -030018int dvb_usb_m920x_debug;
19module_param_named(debug,dvb_usb_m920x_debug, int, 0644);
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -030020MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
21
22static struct dvb_usb_rc_key megasky_rc_keys [] = {
23 { 0x0, 0x12, KEY_POWER },
24 { 0x0, 0x1e, KEY_CYCLEWINDOWS }, /* min/max */
25 { 0x0, 0x02, KEY_CHANNELUP },
26 { 0x0, 0x05, KEY_CHANNELDOWN },
27 { 0x0, 0x03, KEY_VOLUMEUP },
28 { 0x0, 0x06, KEY_VOLUMEDOWN },
29 { 0x0, 0x04, KEY_MUTE },
30 { 0x0, 0x07, KEY_OK }, /* TS */
31 { 0x0, 0x08, KEY_STOP },
32 { 0x0, 0x09, KEY_MENU }, /* swap */
33 { 0x0, 0x0a, KEY_REWIND },
34 { 0x0, 0x1b, KEY_PAUSE },
35 { 0x0, 0x1f, KEY_FASTFORWARD },
36 { 0x0, 0x0c, KEY_RECORD },
37 { 0x0, 0x0d, KEY_CAMERA }, /* screenshot */
38 { 0x0, 0x0e, KEY_COFFEE }, /* "MTS" */
39};
40
41static inline int m9206_read(struct usb_device *udev, u8 request, u16 value, u16 index, void *data, int size)
42{
43 int ret;
44
45 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
46 request, USB_TYPE_VENDOR | USB_DIR_IN,
47 value, index, data, size, 2000);
48 if (ret < 0)
49 return ret;
50
51 if (ret != size)
52 return -EIO;
53
54 return 0;
55}
56
57static inline int m9206_write(struct usb_device *udev, u8 request, u16 value, u16 index)
58{
59 int ret;
60
61 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
62 request, USB_TYPE_VENDOR | USB_DIR_OUT,
63 value, index, NULL, 0, 2000);
64 msleep(3);
65
66 return ret;
67}
68
69static int m9206_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
70{
71 int i, ret = 0;
72 u8 rc_state[2];
73
74 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
75 return -EAGAIN;
76
77 if ((ret = m9206_read(d->udev, 0x22, 0x0, 0xff51, rc_state, 1)) != 0)
78 goto unlock;
79
80 if ((ret = m9206_read(d->udev, 0x22, 0x0, 0xff52, rc_state + 1, 1)) != 0)
81 goto unlock;
82
83 for (i = 0; i < ARRAY_SIZE(megasky_rc_keys); i++)
84 if (megasky_rc_keys[i].data == rc_state[1]) {
85 *event = megasky_rc_keys[i].event;
86
87 switch(rc_state[0]) {
88 case 0x80:
89 *state = REMOTE_NO_KEY_PRESSED;
90 goto unlock;
91
92 case 0x93:
93 case 0x92:
94 *state = REMOTE_KEY_PRESSED;
95 goto unlock;
96
97 case 0x91:
98 *state = REMOTE_KEY_REPEAT;
99 goto unlock;
100
101 default:
102 deb_rc("Unexpected rc response %x\n", rc_state[0]);
103 *state = REMOTE_NO_KEY_PRESSED;
104 goto unlock;
105 }
106 }
107
108 if (rc_state[1] != 0)
109 deb_rc("Unknown rc key %x\n", rc_state[1]);
110
111 *state = REMOTE_NO_KEY_PRESSED;
112
113 unlock:
114 mutex_unlock(&d->i2c_mutex);
115
116 return ret;
117}
118
119/* I2C */
120
121static int m9206_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
122{
123 struct dvb_usb_device *d = i2c_get_adapdata(adap);
124 int i;
125 int ret = 0;
126
127 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
128 return -EAGAIN;
129
130 if (num > 2)
131 return -EINVAL;
132
133 for (i = 0; i < num; i++) {
134 u8 w_len;
135
136 if ((ret = m9206_write(d->udev, 0x23, msg[i].addr, 0x80)) != 0)
137 goto unlock;
138
139 if ((ret = m9206_write(d->udev, 0x23, msg[i].buf[0], 0x0)) != 0)
140 goto unlock;
141
142 if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) {
143 if (msg[i].addr == 0x1e)
144 w_len = 0x1f;
145 else
146 w_len = 0xc5;
147
148 if ((ret = m9206_write(d->udev, 0x23, w_len, 0x80)) != 0)
149 goto unlock;
150
151 if ((ret = m9206_read(d->udev, 0x23, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0)
152 goto unlock;
153
154 i++;
155 } else {
156 if (msg[i].len != 2)
157 return -EINVAL;
158
159 if ((ret = m9206_write(d->udev, 0x23, msg[i].buf[1], 0x40)) != 0)
160 goto unlock;
161 }
162 }
163 ret = i;
164 unlock:
165 mutex_unlock(&d->i2c_mutex);
166
167 return ret;
168}
169
170static u32 m9206_i2c_func(struct i2c_adapter *adapter)
171{
172 return I2C_FUNC_I2C;
173}
174
175static struct i2c_algorithm m9206_i2c_algo = {
176 .master_xfer = m9206_i2c_xfer,
177 .functionality = m9206_i2c_func,
178};
179
180/* Callbacks for DVB USB */
181static int megasky_identify_state (struct usb_device *udev,
182 struct dvb_usb_properties *props,
183 struct dvb_usb_device_description **desc,
184 int *cold)
185{
186 struct usb_host_interface *alt;
187
188 alt = usb_altnum_to_altsetting(usb_ifnum_to_if(udev, 0), 1);
189 *cold = (alt == NULL) ? 1 : 0;
190
191 return 0;
192}
193
194static int megasky_mt352_demod_init(struct dvb_frontend *fe)
195{
196 int i;
197 static u8 buf1[] = {
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300198 CONFIG, 0x3d,
199 CLOCK_CTL, 0x30,
200 RESET, 0x80,
201 ADC_CTL_1, 0x40,
202 AGC_TARGET, 0x1c,
203 AGC_CTL, 0x20,
204 0x69, 0x00,
205 0x6a, 0xff,
206 0x6b, 0xff,
207 0x6c, 0x40,
208 0x6d, 0xff,
209 0x6e, 0x00,
210 0x6f, 0x40,
211 0x70, 0x40,
212 0x93, 0x1a,
213 0xb5, 0x7a,
214 ACQ_CTL, 0x50,
215 INPUT_FREQ_1, 0x31,
216 INPUT_FREQ_0, 0x05,
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300217 };
218
219 for (i = 0; i < ARRAY_SIZE(buf1); i += 2)
220 mt352_write(fe, &buf1[i], 2);
221
222 deb_rc("Demod init!\n");
223
224 return 0;
225}
226
227struct mt352_state;
228
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300229#define W 0
230#define R 1
231/* Not actual hw limits. */
232#define QT1010_MIN_STEP 2000000
233#define QT1010_MIN_FREQ 48000000
234
235int qt1010_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len)
236{
237 int i;
238 int div, mod;
239 struct {
240 u8 read, reg, value;
241 } rd[46] = { { W, 0x01, 0x80 },
242 { W, 0x02, 0x3f },
243 { W, 0x05, 0xff }, /* c */
244 { W, 0x06, 0x44 },
245 { W, 0x07, 0xff }, /* c */
246 { W, 0x08, 0x08 },
247 { W, 0x09, 0xff }, /* c */
248 { W, 0x0a, 0xff }, /* c */
249 { W, 0x0b, 0xff }, /* c */
250 { W, 0x0c, 0xe1 },
251 { W, 0x1a, 0xff }, /* 10 c */
252 { W, 0x1b, 0x00 },
253 { W, 0x1c, 0x89 },
254 { W, 0x11, 0xff }, /* c */
255 { W, 0x12, 0x91 },
256 { W, 0x22, 0xff }, /* c */
257 { W, 0x1e, 0x00 },
258 { W, 0x1e, 0xd0 },
259 { R, 0x22, 0xff }, /* c read */
260 { W, 0x1e, 0x00 },
261 { R, 0x05, 0xff }, /* 20 c read */
262 { R, 0x22, 0xff }, /* c read */
263 { W, 0x23, 0xd0 },
264 { W, 0x1e, 0x00 },
265 { W, 0x1e, 0xe0 },
266 { R, 0x23, 0xff }, /* c read */
267 { W, 0x1e, 0x00 },
268 { W, 0x24, 0xd0 },
269 { W, 0x1e, 0x00 },
270 { W, 0x1e, 0xf0 },
271 { R, 0x24, 0xff }, /* 30 c read */
272 { W, 0x1e, 0x00 },
273 { W, 0x14, 0x7f },
274 { W, 0x15, 0x7f },
275 { W, 0x05, 0xff }, /* c */
276 { W, 0x06, 0x00 },
277 { W, 0x15, 0x1f },
278 { W, 0x16, 0xff },
279 { W, 0x18, 0xff },
280 { W, 0x1f, 0xff }, /* c */
281 { W, 0x20, 0xff }, /* 40 c */
282 { W, 0x21, 0x53 },
283 { W, 0x25, 0xbd },
284 { W, 0x26, 0x15 },
285 { W, 0x02, 0x00 },
286 { W, 0x01, 0x00 },
287 };
288 struct i2c_msg msg;
289 struct dvb_usb_device *d = fe->dvb->priv;
290 unsigned long freq = params->frequency;
291
292 if (freq % QT1010_MIN_STEP)
293 deb_rc("frequency not supported.\n");
294
295 (void) buf;
296 (void) buf_len;
297
298 div = (freq - QT1010_MIN_FREQ) / QT1010_MIN_STEP;
299 mod = (div + 16 - 9) % 16;
300
301 /* 0x5 */
302 if (div >= 377)
303 rd[2].value = 0x74;
304 else if (div >= 265)
305 rd[2].value = 0x54;
306 else if (div >= 121)
307 rd[2].value = 0x34;
308 else
309 rd[2].value = 0x14;
310
311 /* 0x7 */
312 rd[4].value = (((freq - QT1010_MIN_FREQ) / 1000000) * 9975 + 12960000) / 320000;
313
314 /* 09 */
315 if (mod < 4)
316 rd[6].value = 0x1d;
317 else
318 rd[6].value = 0x1c;
319
320 /* 0a */
321 if (mod < 2)
322 rd[7].value = 0x09;
323 else if (mod < 4)
324 rd[7].value = 0x08;
325 else if (mod < 6)
326 rd[7].value = 0x0f;
327 else if (mod < 8)
328 rd[7].value = 0x0e;
329 else if (mod < 10)
330 rd[7].value = 0x0d;
331 else if (mod < 12)
332 rd[7].value = 0x0c;
333 else if (mod < 14)
334 rd[7].value = 0x0b;
335 else
336 rd[7].value = 0x0a;
337
338 /* 0b */
339 if (div & 1)
340 rd[8].value = 0x45;
341 else
342 rd[8].value = 0x44;
343
344 /* 1a */
345 if (div & 1)
346 rd[10].value = 0x78;
347 else
348 rd[10].value = 0xf8;
349
350 /* 11 */
351 if (div >= 265)
352 rd[13].value = 0xf9;
353 else if (div >= 121)
354 rd[13].value = 0xfd;
355 else
356 rd[13].value = 0xf9;
357
358 /* 22 */
359 if (div < 201)
360 rd[15].value = 0xd0;
361 else if (div < 217)
362 rd[15].value = 0xd3;
363 else if (div < 233)
364 rd[15].value = 0xd6;
365 else if (div < 249)
366 rd[15].value = 0xd9;
367 else if (div < 265)
368 rd[15].value = 0xda;
369 else
370 rd[15].value = 0xd0;
371
372 /* 05 */
373 if (div >= 377)
374 rd[34].value = 0x70;
375 else if (div >= 265)
376 rd[34].value = 0x50;
377 else if (div >= 121)
378 rd[34].value = 0x30;
379 else
380 rd[34].value = 0x10;
381
382 /* 1f */
383 if (mod < 4)
384 rd[39].value = 0x64;
385 else if (mod < 6)
386 rd[39].value = 0x66;
387 else if (mod < 8)
388 rd[39].value = 0x67;
389 else if (mod < 12)
390 rd[39].value = 0x68;
391 else if (mod < 14)
392 rd[39].value = 0x69;
393 else
394 rd[39].value = 0x6a;
395
396 /* 20 */
397 if (mod < 4)
398 rd[40].value = 0x10;
399 else if (mod < 6)
400 rd[40].value = 0x11;
401 else if (mod < 10)
402 rd[40].value = 0x12;
403 else if (mod < 12)
404 rd[40].value = 0x13;
405 else if (mod < 14)
406 rd[40].value = 0x14;
407 else
408 rd[40].value = 0x15;
409
410 deb_rc("Now tuning... ");
411 for (i = 0; i < sizeof(rd) / sizeof(*rd); i++) {
412 if (rd[i].read)
413 continue;
414
415 msg.flags = 0;
416 msg.len = 2;
417 msg.addr = 0xc4;
418 msg.buf = &rd[i].reg;
419
420 if (i2c_transfer(&d->i2c_adap, &msg, 1) != 1) {
421 deb_rc("tuner write failed\n");
422 return -EIO;
423 }
424 }
425 deb_rc("done\n");
426
427 return 0;
428}
429#undef W
430#undef R
431
432static struct mt352_config megasky_mt352_config = {
433 .demod_address = 0x1e,
434 .demod_init = megasky_mt352_demod_init,
435};
436
437static int megasky_frontend_attach(struct dvb_usb_device *d)
438{
439 deb_rc("megasky_frontend_attach!\n");
440
441 if ((d->fe = mt352_attach(&megasky_mt352_config, &d->i2c_adap)) != NULL) {
442 d->fe->ops.tuner_ops.calc_regs = qt1010_set_params;
443 return 0;
444 }
445 return -EIO;
446}
447
448/* DVB USB Driver stuff */
449static struct dvb_usb_properties megasky_properties;
450
451static int megasky_probe(struct usb_interface *intf, const struct usb_device_id *id)
452{
453 struct dvb_usb_device *d;
454 struct usb_host_interface *alt;
455 int ret;
456
457 if ((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) {
458 deb_rc("probed!\n");
459
460 alt = usb_altnum_to_altsetting(intf, 1);
461 if (alt == NULL) {
462 deb_rc("not alt found!\n");
463 return -ENODEV;
464 }
465
466 ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, alt->desc.bAlternateSetting);
467 if (ret < 0)
468 return ret;
469
470 deb_rc("Changed to alternate setting!\n");
471
472 /* Remote controller init. */
473 if ((ret = m9206_write(d->udev, 0x22, 0xa8, 0xff55)) != 0)
474 return ret;
475
476 if ((ret = m9206_write(d->udev, 0x22, 0x51, 0xff54)) != 0)
477 return ret;
478 }
479 return ret;
480}
481
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300482static struct usb_device_id m920x_table [] = {
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300483 { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) },
484 { } /* Terminating entry */
485};
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300486MODULE_DEVICE_TABLE (usb, m920x_table);
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300487
488static int set_filter(struct dvb_usb_device *d, int type, int idx, int pid)
489{
490 int ret = 0;
491
492 if (pid >= 0x8000)
493 return -EINVAL;
494
495 pid |= 0x8000;
496
497 if ((ret = m9206_write(d->udev, 0x25, pid, (type << 8) | (idx * 4) )) != 0)
498 return ret;
499
500 if ((ret = m9206_write(d->udev, 0x25, 0, (type << 8) | (idx * 4) )) != 0)
501 return ret;
502
503 return ret;
504}
505
506static int m9206_pid_filter_ctrl(struct dvb_usb_device *d, int onoff)
507{
508 int ret = 0;
509
510 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
511 return -EAGAIN;
512
513 deb_rc("filtering %s\n", onoff ? "on" : "off");
514 if (onoff == 0) {
515 if ((ret = set_filter(d, 0x81, 1, 0x00)) != 0)
516 goto unlock;
517
518 if ((ret = set_filter(d, 0x82, 0, 0x02f5)) != 0)
519 goto unlock;
520 }
521 unlock:
522 mutex_unlock(&d->i2c_mutex);
523
524 return ret;
525}
526
527static int m9206_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff)
528{
529 int ret = 0;
530
531 if (pid == 8192)
532 return m9206_pid_filter_ctrl(d, !onoff);
533
534 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
535 return -EAGAIN;
536
537 deb_rc("filter %d, pid %x, %s\n", index, pid, onoff ? "on" : "off");
538 if (onoff == 0)
539 pid = 0;
540
541 if ((ret = set_filter(d, 0x81, 1, 0x01)) != 0)
542 goto unlock;
543
544 if ((ret = set_filter(d, 0x81, index + 2, pid)) != 0)
545 goto unlock;
546
547 if ((ret = set_filter(d, 0x82, 0, 0x02f5)) != 0)
548 goto unlock;
549
550 unlock:
551 mutex_unlock(&d->i2c_mutex);
552
553 return ret;
554}
555
556static int m9206_firmware_download(struct usb_device *udev, const struct firmware *fw)
557{
558 u16 value, index, size;
559 u8 read[4], *buff;
560 int i, pass, ret = 0;
561
562 buff = kmalloc(65536, GFP_KERNEL);
563
564 if ((ret = m9206_read(udev, 0x25, 0x0, 0x8000, read, 4)) != 0)
565 goto done;
566 deb_rc("%x %x %x %x\n", read[0], read[1], read[2], read[3]);
567
568 if ((ret = m9206_read(udev, 0x30, 0x0, 0x0, read, 1)) != 0)
569 goto done;
570 deb_rc("%x\n", read[0]);
571
572 for (pass = 0; pass < 2; pass++) {
573 for (i = 0; i + (sizeof(u16) * 3) < fw->size;) {
574 value = le16_to_cpu(*(u16 *)(fw->data + i));
575 i += sizeof(u16);
576
577 index = le16_to_cpu(*(u16 *)(fw->data + i));
578 i += sizeof(u16);
579
580 size = le16_to_cpu(*(u16 *)(fw->data + i));
581 i += sizeof(u16);
582
583 if (pass == 1) {
584 /* Will stall if using fw->data ... */
585 memcpy(buff, fw->data + i, size);
586
587 ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0),
588 0x30, USB_TYPE_VENDOR | USB_DIR_OUT,
589 value, index, buff, size, 20);
590 if (ret != size) {
591 deb_rc("error while uploading fw!\n");
592 ret = -EIO;
593 goto done;
594 }
595 msleep(3);
596 }
597 i += size;
598 }
599 if (i != fw->size) {
600 ret = -EINVAL;
601 goto done;
602 }
603 }
604
605 msleep(36);
606
607 /* m9206 will disconnect itself from the bus after this. */
608 (void) m9206_write(udev, 0x22, 0x01, 0xff69);
609 deb_rc("firmware uploaded!\n");
610
611 done:
612 kfree(buff);
613
614 return ret;
615}
616
617static struct dvb_usb_properties megasky_properties = {
618 .caps = DVB_USB_IS_AN_I2C_ADAPTER | DVB_USB_HAS_PID_FILTER |
619 DVB_USB_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_NEED_PID_FILTERING,
620 .pid_filter_count = 8,
621
622 .usb_ctrl = DEVICE_SPECIFIC,
623 .firmware = "dvb-usb-megasky-02.fw",
624 .download_firmware = m9206_firmware_download,
625
626 .pid_filter = m9206_pid_filter,
627 .pid_filter_ctrl = m9206_pid_filter_ctrl,
628 .frontend_attach = megasky_frontend_attach,
629
630 .rc_interval = 200,
631 .rc_key_map = megasky_rc_keys,
632 .rc_key_map_size = ARRAY_SIZE(megasky_rc_keys),
633 .rc_query = m9206_rc_query,
634
635 .size_of_priv = 0,
636
637 .identify_state = megasky_identify_state,
638 .i2c_algo = &m9206_i2c_algo,
639
640 .generic_bulk_ctrl_endpoint = 0x01,
641 .urb = {
642 .type = DVB_USB_BULK,
643 .count = 8,
644 .endpoint = 0x81,
645 .u = {
646 .bulk = {
647 .buffersize = 512,
648 }
649 }
650 },
651 .num_device_descs = 1,
652 .devices = {
653 { "MSI Mega Sky 580 DVB-T USB2.0",
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300654 { &m920x_table[0], NULL },
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300655 { NULL },
656 },
657 { NULL },
658 }
659};
660
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300661static struct usb_driver m920x_driver = {
662 .name = "dvb_usb_m920x",
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300663 .probe = megasky_probe,
664 .disconnect = dvb_usb_device_exit,
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300665 .id_table = m920x_table,
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300666};
667
668/* module stuff */
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300669static int __init m920x_module_init(void)
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300670{
671 int ret;
672
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300673 if ((ret = usb_register(&m920x_driver))) {
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300674 err("usb_register failed. Error number %d", ret);
675 return ret;
676 }
677
678 return 0;
679}
680
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300681static void __exit m920x_module_exit(void)
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300682{
683 /* deregister this driver from the USB subsystem */
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300684 usb_deregister(&m920x_driver);
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300685}
686
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300687module_init (m920x_module_init);
688module_exit (m920x_module_exit);
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300689
690MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
Michael Krufkybaa2ed02006-09-23 20:01:29 -0300691MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / Uli m920x");
Aapo Tahkola5fecd9f2006-09-23 20:00:41 -0300692MODULE_VERSION("0.1");
693MODULE_LICENSE("GPL");