diff options
-rw-r--r-- | drivers/media/dvb/Kconfig | 4 | ||||
-rw-r--r-- | drivers/media/dvb/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/dvb/dm1105/Kconfig | 14 | ||||
-rw-r--r-- | drivers/media/dvb/dm1105/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/dvb/dm1105/dm1105.c | 917 |
5 files changed, 939 insertions, 1 deletions
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 8bc1445bd33b..5858744e70de 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig | |||
@@ -35,6 +35,10 @@ comment "Supported Pluto2 Adapters" | |||
35 | depends on DVB_CORE && PCI && I2C | 35 | depends on DVB_CORE && PCI && I2C |
36 | source "drivers/media/dvb/pluto2/Kconfig" | 36 | source "drivers/media/dvb/pluto2/Kconfig" |
37 | 37 | ||
38 | comment "Supported SDMC DM1105 Adapters" | ||
39 | depends on DVB_CORE && PCI && I2C | ||
40 | source "drivers/media/dvb/dm1105/Kconfig" | ||
41 | |||
38 | comment "Supported DVB Frontends" | 42 | comment "Supported DVB Frontends" |
39 | depends on DVB_CORE | 43 | depends on DVB_CORE |
40 | source "drivers/media/dvb/frontends/Kconfig" | 44 | source "drivers/media/dvb/frontends/Kconfig" |
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index d6ba4d195201..ea953a03e24d 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile | |||
@@ -2,4 +2,4 @@ | |||
2 | # Makefile for the kernel multimedia device drivers. | 2 | # Makefile for the kernel multimedia device drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ | 5 | obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ dm1105/ |
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig new file mode 100644 index 000000000000..fc2ca7dca6b2 --- /dev/null +++ b/drivers/media/dvb/dm1105/Kconfig | |||
@@ -0,0 +1,14 @@ | |||
1 | config DVB_DM1105 | ||
2 | tristate "SDMC DM1105 based PCI cards" | ||
3 | depends on DVB_CORE && PCI && I2C | ||
4 | select DVB_PLL if !DVB_FE_CUSTOMISE | ||
5 | select DVB_STV0299 if !DVB_FE_CUSTOMISE | ||
6 | help | ||
7 | Support for cards based on the SDMC DM1105 PCI chip like | ||
8 | DvbWorld 2002 | ||
9 | |||
10 | Since these cards have no MPEG decoder onboard, they transmit | ||
11 | only compressed MPEG data over the PCI bus, so you need | ||
12 | an external software decoder to watch TV on your computer. | ||
13 | |||
14 | Say Y or M if you own such a device and want to use it. | ||
diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile new file mode 100644 index 000000000000..8ac28b0546af --- /dev/null +++ b/drivers/media/dvb/dm1105/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_DVB_DM1105) += dm1105.o | ||
2 | |||
3 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends | ||
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c new file mode 100644 index 000000000000..fe8d0e58c0ea --- /dev/null +++ b/drivers/media/dvb/dm1105/dm1105.c | |||
@@ -0,0 +1,917 @@ | |||
1 | /* | ||
2 | * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip | ||
3 | * | ||
4 | * Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by> | ||
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/version.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/proc_fs.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <media/ir-common.h> | ||
32 | |||
33 | #include "demux.h" | ||
34 | #include "dmxdev.h" | ||
35 | #include "dvb_demux.h" | ||
36 | #include "dvb_frontend.h" | ||
37 | #include "dvb_net.h" | ||
38 | #include "dvbdev.h" | ||
39 | #include "dvb-pll.h" | ||
40 | |||
41 | #include "stv0299.h" | ||
42 | /*#include "stv0288.h" | ||
43 | *#include "si21xx.h" | ||
44 | *#include "stb6000.h" | ||
45 | *#include "cx24116.h"*/ | ||
46 | #include "z0194a.h" | ||
47 | |||
48 | /* ----------------------------------------------- */ | ||
49 | /* | ||
50 | * PCI ID's | ||
51 | */ | ||
52 | #ifndef PCI_VENDOR_ID_TRIGEM | ||
53 | #define PCI_VENDOR_ID_TRIGEM 0x109f | ||
54 | #endif | ||
55 | #ifndef PCI_DEVICE_ID_DM1105 | ||
56 | #define PCI_DEVICE_ID_DM1105 0x036f | ||
57 | #endif | ||
58 | #ifndef PCI_DEVICE_ID_DW2002 | ||
59 | #define PCI_DEVICE_ID_DW2002 0x2002 | ||
60 | #endif | ||
61 | #ifndef PCI_DEVICE_ID_DW2004 | ||
62 | #define PCI_DEVICE_ID_DW2004 0x2004 | ||
63 | #endif | ||
64 | /* ----------------------------------------------- */ | ||
65 | /* sdmc dm1105 registers */ | ||
66 | |||
67 | /* TS Control */ | ||
68 | #define DM1105_TSCTR 0x00 | ||
69 | #define DM1105_DTALENTH 0x04 | ||
70 | |||
71 | /* GPIO Interface */ | ||
72 | #define DM1105_GPIOVAL 0x08 | ||
73 | #define DM1105_GPIOCTR 0x0c | ||
74 | |||
75 | /* PID serial number */ | ||
76 | #define DM1105_PIDN 0x10 | ||
77 | |||
78 | /* Odd-even secret key select */ | ||
79 | #define DM1105_CWSEL 0x14 | ||
80 | |||
81 | /* Host Command Interface */ | ||
82 | #define DM1105_HOST_CTR 0x18 | ||
83 | #define DM1105_HOST_AD 0x1c | ||
84 | |||
85 | /* PCI Interface */ | ||
86 | #define DM1105_CR 0x30 | ||
87 | #define DM1105_RST 0x34 | ||
88 | #define DM1105_STADR 0x38 | ||
89 | #define DM1105_RLEN 0x3c | ||
90 | #define DM1105_WRP 0x40 | ||
91 | #define DM1105_INTCNT 0x44 | ||
92 | #define DM1105_INTMAK 0x48 | ||
93 | #define DM1105_INTSTS 0x4c | ||
94 | |||
95 | /* CW Value */ | ||
96 | #define DM1105_ODD 0x50 | ||
97 | #define DM1105_EVEN 0x58 | ||
98 | |||
99 | /* PID Value */ | ||
100 | #define DM1105_PID 0x60 | ||
101 | |||
102 | /* IR Control */ | ||
103 | #define DM1105_IRCTR 0x64 | ||
104 | #define DM1105_IRMODE 0x68 | ||
105 | #define DM1105_SYSTEMCODE 0x6c | ||
106 | #define DM1105_IRCODE 0x70 | ||
107 | |||
108 | /* Unknown Values */ | ||
109 | #define DM1105_ENCRYPT 0x74 | ||
110 | #define DM1105_VER 0x7c | ||
111 | |||
112 | /* I2C Interface */ | ||
113 | #define DM1105_I2CCTR 0x80 | ||
114 | #define DM1105_I2CSTS 0x81 | ||
115 | #define DM1105_I2CDAT 0x82 | ||
116 | #define DM1105_I2C_RA 0x83 | ||
117 | /* ----------------------------------------------- */ | ||
118 | /* Interrupt Mask Bits */ | ||
119 | |||
120 | #define INTMAK_TSIRQM 0x01 | ||
121 | #define INTMAK_HIRQM 0x04 | ||
122 | #define INTMAK_IRM 0x08 | ||
123 | #define INTMAK_ALLMASK (INTMAK_TSIRQM | \ | ||
124 | INTMAK_HIRQM | \ | ||
125 | INTMAK_IRM) | ||
126 | #define INTMAK_NONEMASK 0x00 | ||
127 | |||
128 | /* Interrupt Status Bits */ | ||
129 | #define INTSTS_TSIRQ 0x01 | ||
130 | #define INTSTS_HIRQ 0x04 | ||
131 | #define INTSTS_IR 0x08 | ||
132 | |||
133 | /* IR Control Bits */ | ||
134 | #define DM1105_IR_EN 0x01 | ||
135 | #define DM1105_SYS_CHK 0x02 | ||
136 | #define DM1105_REP_FLG 0x08 | ||
137 | |||
138 | /* EEPROM addr */ | ||
139 | #define IIC_24C01_addr 0xa0 | ||
140 | /* Max board count */ | ||
141 | #define DM1105_MAX 0x04 | ||
142 | |||
143 | #define DRIVER_NAME "dm1105" | ||
144 | |||
145 | #define DM1105_DMA_PACKETS 47 | ||
146 | #define DM1105_DMA_PACKET_LENGTH (128*4) | ||
147 | #define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) | ||
148 | |||
149 | /* GPIO's for LNB power control */ | ||
150 | #define DM1105_LNB_MASK 0x00000000 | ||
151 | #define DM1105_LNB_13V 0x00010100 | ||
152 | #define DM1105_LNB_18V 0x00000100 | ||
153 | |||
154 | static int ir_debug; | ||
155 | module_param(ir_debug, int, 0644); | ||
156 | MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); | ||
157 | |||
158 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
159 | |||
160 | static u16 ir_codes_dm1105_nec[128] = { | ||
161 | [0x0a] = KEY_Q, /*power*/ | ||
162 | [0x0c] = KEY_M, /*mute*/ | ||
163 | [0x11] = KEY_1, | ||
164 | [0x12] = KEY_2, | ||
165 | [0x13] = KEY_3, | ||
166 | [0x14] = KEY_4, | ||
167 | [0x15] = KEY_5, | ||
168 | [0x16] = KEY_6, | ||
169 | [0x17] = KEY_7, | ||
170 | [0x18] = KEY_8, | ||
171 | [0x19] = KEY_9, | ||
172 | [0x10] = KEY_0, | ||
173 | [0x1c] = KEY_PAGEUP, /*ch+*/ | ||
174 | [0x0f] = KEY_PAGEDOWN, /*ch-*/ | ||
175 | [0x1a] = KEY_O, /*vol+*/ | ||
176 | [0x0e] = KEY_Z, /*vol-*/ | ||
177 | [0x04] = KEY_R, /*rec*/ | ||
178 | [0x09] = KEY_D, /*fav*/ | ||
179 | [0x08] = KEY_BACKSPACE, /*rewind*/ | ||
180 | [0x07] = KEY_A, /*fast*/ | ||
181 | [0x0b] = KEY_P, /*pause*/ | ||
182 | [0x02] = KEY_ESC, /*cancel*/ | ||
183 | [0x03] = KEY_G, /*tab*/ | ||
184 | [0x00] = KEY_UP, /*up*/ | ||
185 | [0x1f] = KEY_ENTER, /*ok*/ | ||
186 | [0x01] = KEY_DOWN, /*down*/ | ||
187 | [0x05] = KEY_C, /*cap*/ | ||
188 | [0x06] = KEY_S, /*stop*/ | ||
189 | [0x40] = KEY_F, /*full*/ | ||
190 | [0x1e] = KEY_W, /*tvmode*/ | ||
191 | [0x1b] = KEY_B, /*recall*/ | ||
192 | }; | ||
193 | |||
194 | /* infrared remote control */ | ||
195 | struct infrared { | ||
196 | u16 key_map[128]; | ||
197 | struct input_dev *input_dev; | ||
198 | char input_phys[32]; | ||
199 | struct tasklet_struct ir_tasklet; | ||
200 | u32 ir_command; | ||
201 | }; | ||
202 | |||
203 | struct dm1105dvb { | ||
204 | /* pci */ | ||
205 | struct pci_dev *pdev; | ||
206 | u8 __iomem *io_mem; | ||
207 | |||
208 | /* ir */ | ||
209 | struct infrared ir; | ||
210 | |||
211 | /* dvb */ | ||
212 | struct dmx_frontend hw_frontend; | ||
213 | struct dmx_frontend mem_frontend; | ||
214 | struct dmxdev dmxdev; | ||
215 | struct dvb_adapter dvb_adapter; | ||
216 | struct dvb_demux demux; | ||
217 | struct dvb_frontend *fe; | ||
218 | struct dvb_net dvbnet; | ||
219 | unsigned int full_ts_users; | ||
220 | |||
221 | /* i2c */ | ||
222 | struct i2c_adapter i2c_adap; | ||
223 | |||
224 | /* dma */ | ||
225 | dma_addr_t dma_addr; | ||
226 | unsigned char *ts_buf; | ||
227 | u32 wrp; | ||
228 | u32 buffer_size; | ||
229 | unsigned int PacketErrorCount; | ||
230 | unsigned int dmarst; | ||
231 | spinlock_t lock; | ||
232 | |||
233 | }; | ||
234 | |||
235 | #define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg])) | ||
236 | |||
237 | static struct dm1105dvb *dm1105dvb_local; | ||
238 | |||
239 | static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, | ||
240 | struct i2c_msg *msgs, int num) | ||
241 | { | ||
242 | struct dm1105dvb *dm1105dvb ; | ||
243 | |||
244 | int addr, rc, i, j, k, len, byte, data; | ||
245 | u8 status; | ||
246 | |||
247 | dm1105dvb = i2c_adap->algo_data; | ||
248 | for (i = 0; i < num; i++) { | ||
249 | outb(0x00, dm_io_mem(DM1105_I2CCTR)); | ||
250 | if (msgs[i].flags & I2C_M_RD) { | ||
251 | /* read bytes */ | ||
252 | addr = msgs[i].addr << 1; | ||
253 | addr |= 1; | ||
254 | outb(addr, dm_io_mem(DM1105_I2CDAT)); | ||
255 | for (byte = 0; byte < msgs[i].len; byte++) | ||
256 | outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1)); | ||
257 | |||
258 | outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); | ||
259 | for (j = 0; j < 55; j++) { | ||
260 | mdelay(10); | ||
261 | status = inb(dm_io_mem(DM1105_I2CSTS)); | ||
262 | if ((status & 0xc0) == 0x40) | ||
263 | break; | ||
264 | } | ||
265 | if (j >= 55) | ||
266 | return -1; | ||
267 | |||
268 | for (byte = 0; byte < msgs[i].len; byte++) { | ||
269 | rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1)); | ||
270 | if (rc < 0) | ||
271 | goto err; | ||
272 | msgs[i].buf[byte] = rc; | ||
273 | } | ||
274 | } else { | ||
275 | if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { | ||
276 | /* prepaired for cx24116 firmware */ | ||
277 | /* Write in small blocks */ | ||
278 | len = msgs[i].len - 1; | ||
279 | k = 1; | ||
280 | do { | ||
281 | outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT)); | ||
282 | outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1)); | ||
283 | for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { | ||
284 | data = msgs[i].buf[k+byte]; | ||
285 | outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2)); | ||
286 | } | ||
287 | outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR)); | ||
288 | for (j = 0; j < 25; j++) { | ||
289 | mdelay(10); | ||
290 | status = inb(dm_io_mem(DM1105_I2CSTS)); | ||
291 | if ((status & 0xc0) == 0x40) | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | if (j >= 25) | ||
296 | return -1; | ||
297 | |||
298 | k += 48; | ||
299 | len -= 48; | ||
300 | } while (len > 0); | ||
301 | } else { | ||
302 | /* write bytes */ | ||
303 | outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT)); | ||
304 | for (byte = 0; byte < msgs[i].len; byte++) { | ||
305 | data = msgs[i].buf[byte]; | ||
306 | outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1)); | ||
307 | } | ||
308 | outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); | ||
309 | for (j = 0; j < 25; j++) { | ||
310 | mdelay(10); | ||
311 | status = inb(dm_io_mem(DM1105_I2CSTS)); | ||
312 | if ((status & 0xc0) == 0x40) | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | if (j >= 25) | ||
317 | return -1; | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | return num; | ||
322 | err: | ||
323 | return rc; | ||
324 | } | ||
325 | |||
326 | static u32 functionality(struct i2c_adapter *adap) | ||
327 | { | ||
328 | return I2C_FUNC_I2C; | ||
329 | } | ||
330 | |||
331 | static struct i2c_algorithm dm1105_algo = { | ||
332 | .master_xfer = dm1105_i2c_xfer, | ||
333 | .functionality = functionality, | ||
334 | }; | ||
335 | |||
336 | static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed) | ||
337 | { | ||
338 | return container_of(feed->demux, struct dm1105dvb, demux); | ||
339 | } | ||
340 | |||
341 | static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe) | ||
342 | { | ||
343 | return container_of(fe->dvb, struct dm1105dvb, dvb_adapter); | ||
344 | } | ||
345 | |||
346 | static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) | ||
347 | { | ||
348 | struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe); | ||
349 | |||
350 | if (voltage == SEC_VOLTAGE_18) { | ||
351 | outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR)); | ||
352 | outl(DM1105_LNB_18V, dm_io_mem(DM1105_GPIOVAL)); | ||
353 | } else { | ||
354 | /*LNB ON-13V by default!*/ | ||
355 | outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR)); | ||
356 | outl(DM1105_LNB_13V, dm_io_mem(DM1105_GPIOVAL)); | ||
357 | } | ||
358 | |||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb) | ||
363 | { | ||
364 | outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR)); | ||
365 | } | ||
366 | |||
367 | static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb) | ||
368 | { | ||
369 | dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr); | ||
370 | |||
371 | return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr); | ||
372 | } | ||
373 | |||
374 | static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb) | ||
375 | { | ||
376 | pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr); | ||
377 | } | ||
378 | |||
379 | static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb) | ||
380 | { | ||
381 | outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK)); | ||
382 | outb(1, dm_io_mem(DM1105_CR)); | ||
383 | } | ||
384 | |||
385 | static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb) | ||
386 | { | ||
387 | outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK)); | ||
388 | outb(0, dm_io_mem(DM1105_CR)); | ||
389 | } | ||
390 | |||
391 | static int dm1105dvb_start_feed(struct dvb_demux_feed *f) | ||
392 | { | ||
393 | struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); | ||
394 | |||
395 | if (dm1105dvb->full_ts_users++ == 0) | ||
396 | dm1105dvb_enable_irqs(dm1105dvb); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int dm1105dvb_stop_feed(struct dvb_demux_feed *f) | ||
402 | { | ||
403 | struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); | ||
404 | |||
405 | if (--dm1105dvb->full_ts_users == 0) | ||
406 | dm1105dvb_disable_irqs(dm1105dvb); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | /* ir tasklet */ | ||
412 | static void dm1105_emit_key(unsigned long parm) | ||
413 | { | ||
414 | struct infrared *ir = (struct infrared *) parm; | ||
415 | u32 ircom = ir->ir_command; | ||
416 | u8 data; | ||
417 | u16 keycode; | ||
418 | |||
419 | data = (ircom >> 8) & 0x7f; | ||
420 | |||
421 | input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data); | ||
422 | input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); | ||
423 | keycode = ir->key_map[data]; | ||
424 | |||
425 | if (!keycode) | ||
426 | return; | ||
427 | |||
428 | input_event(ir->input_dev, EV_KEY, keycode, 1); | ||
429 | input_sync(ir->input_dev); | ||
430 | input_event(ir->input_dev, EV_KEY, keycode, 0); | ||
431 | input_sync(ir->input_dev); | ||
432 | |||
433 | } | ||
434 | |||
435 | static irqreturn_t dm1105dvb_irq(int irq, void *dev_id) | ||
436 | { | ||
437 | struct dm1105dvb *dm1105dvb = dev_id; | ||
438 | unsigned int piece; | ||
439 | unsigned int nbpackets; | ||
440 | u32 command; | ||
441 | u32 nextwrp; | ||
442 | u32 oldwrp; | ||
443 | |||
444 | /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ | ||
445 | unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS)); | ||
446 | outb(intsts, dm_io_mem(DM1105_INTSTS)); | ||
447 | |||
448 | switch (intsts) { | ||
449 | case INTSTS_TSIRQ: | ||
450 | case (INTSTS_TSIRQ | INTSTS_IR): | ||
451 | nextwrp = inl(dm_io_mem(DM1105_WRP)) - | ||
452 | inl(dm_io_mem(DM1105_STADR)) ; | ||
453 | oldwrp = dm1105dvb->wrp; | ||
454 | spin_lock(&dm1105dvb->lock); | ||
455 | if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) && | ||
456 | (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) && | ||
457 | (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) { | ||
458 | dm1105dvb->PacketErrorCount++; | ||
459 | /* bad packet found */ | ||
460 | if ((dm1105dvb->PacketErrorCount >= 2) && | ||
461 | (dm1105dvb->dmarst == 0)) { | ||
462 | outb(1, dm_io_mem(DM1105_RST)); | ||
463 | dm1105dvb->wrp = 0; | ||
464 | dm1105dvb->PacketErrorCount = 0; | ||
465 | dm1105dvb->dmarst = 0; | ||
466 | spin_unlock(&dm1105dvb->lock); | ||
467 | return IRQ_HANDLED; | ||
468 | } | ||
469 | } | ||
470 | if (nextwrp < oldwrp) { | ||
471 | piece = dm1105dvb->buffer_size - oldwrp; | ||
472 | memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp); | ||
473 | nbpackets = (piece + nextwrp)/188; | ||
474 | } else { | ||
475 | nbpackets = (nextwrp - oldwrp)/188; | ||
476 | } | ||
477 | dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets); | ||
478 | dm1105dvb->wrp = nextwrp; | ||
479 | spin_unlock(&dm1105dvb->lock); | ||
480 | break; | ||
481 | case INTSTS_IR: | ||
482 | command = inl(dm_io_mem(DM1105_IRCODE)); | ||
483 | if (ir_debug) | ||
484 | printk("dm1105: received byte 0x%04x\n", command); | ||
485 | |||
486 | dm1105dvb->ir.ir_command = command; | ||
487 | tasklet_schedule(&dm1105dvb->ir.ir_tasklet); | ||
488 | break; | ||
489 | } | ||
490 | return IRQ_HANDLED; | ||
491 | |||
492 | |||
493 | } | ||
494 | |||
495 | /* register with input layer */ | ||
496 | static void input_register_keys(struct infrared *ir) | ||
497 | { | ||
498 | int i; | ||
499 | |||
500 | memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); | ||
501 | |||
502 | for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) | ||
503 | set_bit(ir->key_map[i], ir->input_dev->keybit); | ||
504 | |||
505 | ir->input_dev->keycode = ir->key_map; | ||
506 | ir->input_dev->keycodesize = sizeof(ir->key_map[0]); | ||
507 | ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); | ||
508 | } | ||
509 | |||
510 | int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) | ||
511 | { | ||
512 | struct input_dev *input_dev; | ||
513 | int err; | ||
514 | |||
515 | dm1105dvb_local = dm1105; | ||
516 | |||
517 | input_dev = input_allocate_device(); | ||
518 | if (!input_dev) | ||
519 | return -ENOMEM; | ||
520 | |||
521 | dm1105->ir.input_dev = input_dev; | ||
522 | snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), | ||
523 | "pci-%s/ir0", pci_name(dm1105->pdev)); | ||
524 | |||
525 | input_dev->evbit[0] = BIT(EV_KEY); | ||
526 | input_dev->name = "DVB on-card IR receiver"; | ||
527 | |||
528 | input_dev->phys = dm1105->ir.input_phys; | ||
529 | input_dev->id.bustype = BUS_PCI; | ||
530 | input_dev->id.version = 2; | ||
531 | if (dm1105->pdev->subsystem_vendor) { | ||
532 | input_dev->id.vendor = dm1105->pdev->subsystem_vendor; | ||
533 | input_dev->id.product = dm1105->pdev->subsystem_device; | ||
534 | } else { | ||
535 | input_dev->id.vendor = dm1105->pdev->vendor; | ||
536 | input_dev->id.product = dm1105->pdev->device; | ||
537 | } | ||
538 | input_dev->dev.parent = &dm1105->pdev->dev; | ||
539 | /* initial keymap */ | ||
540 | memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map); | ||
541 | input_register_keys(&dm1105->ir); | ||
542 | err = input_register_device(input_dev); | ||
543 | if (err) { | ||
544 | input_free_device(input_dev); | ||
545 | return err; | ||
546 | } | ||
547 | |||
548 | tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | |||
554 | void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105) | ||
555 | { | ||
556 | tasklet_kill(&dm1105->ir.ir_tasklet); | ||
557 | input_unregister_device(dm1105->ir.input_dev); | ||
558 | |||
559 | } | ||
560 | |||
561 | static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) | ||
562 | { | ||
563 | dm1105dvb_disable_irqs(dm1105dvb); | ||
564 | |||
565 | outb(0, dm_io_mem(DM1105_HOST_CTR)); | ||
566 | |||
567 | /*DATALEN 188,*/ | ||
568 | outb(188, dm_io_mem(DM1105_DTALENTH)); | ||
569 | /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ | ||
570 | outw(0xc10a, dm_io_mem(DM1105_TSCTR)); | ||
571 | |||
572 | /* map DMA and set address */ | ||
573 | dm1105dvb_dma_map(dm1105dvb); | ||
574 | dm1105dvb_set_dma_addr(dm1105dvb); | ||
575 | /* big buffer */ | ||
576 | outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN)); | ||
577 | outb(47, dm_io_mem(DM1105_INTCNT)); | ||
578 | |||
579 | /* IR NEC mode enable */ | ||
580 | outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR)); | ||
581 | outb(0, dm_io_mem(DM1105_IRMODE)); | ||
582 | outw(0, dm_io_mem(DM1105_SYSTEMCODE)); | ||
583 | |||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb) | ||
588 | { | ||
589 | dm1105dvb_disable_irqs(dm1105dvb); | ||
590 | |||
591 | /* IR disable */ | ||
592 | outb(0, dm_io_mem(DM1105_IRCTR)); | ||
593 | outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK)); | ||
594 | |||
595 | dm1105dvb_dma_unmap(dm1105dvb); | ||
596 | } | ||
597 | #if 0 | ||
598 | static struct stv0288_config earda_config = { | ||
599 | .demod_address = 0x68, | ||
600 | .min_delay_ms = 100, | ||
601 | }; | ||
602 | |||
603 | static struct si21xx_config serit_config = { | ||
604 | .demod_address = 0x68, | ||
605 | .min_delay_ms = 100, | ||
606 | |||
607 | }; | ||
608 | |||
609 | static struct cx24116_config serit_sp2633_config = { | ||
610 | .demod_address = 0x55, | ||
611 | }; | ||
612 | #endif /* keep */ | ||
613 | |||
614 | static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) | ||
615 | { | ||
616 | int ret; | ||
617 | |||
618 | switch (dm1105dvb->pdev->subsystem_device) { | ||
619 | case PCI_DEVICE_ID_DW2002: | ||
620 | dm1105dvb->fe = dvb_attach( | ||
621 | stv0299_attach, &sharp_z0194a_config, | ||
622 | &dm1105dvb->i2c_adap); | ||
623 | |||
624 | if (dm1105dvb->fe) { | ||
625 | dm1105dvb->fe->ops.set_voltage = | ||
626 | dm1105dvb_set_voltage; | ||
627 | dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60, | ||
628 | &dm1105dvb->i2c_adap, DVB_PLL_OPERA1); | ||
629 | } | ||
630 | #if 0 | ||
631 | if (!dm1105dvb->fe) { | ||
632 | dm1105dvb->fe = dvb_attach( | ||
633 | stv0288_attach, &earda_config, | ||
634 | &dm1105dvb->i2c_adap); | ||
635 | if (dm1105dvb->fe) { | ||
636 | dm1105dvb->fe->ops.set_voltage = | ||
637 | dm1105dvb_set_voltage; | ||
638 | dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61, | ||
639 | &dm1105dvb->i2c_adap); | ||
640 | } | ||
641 | } | ||
642 | |||
643 | if (!dm1105dvb->fe) { | ||
644 | dm1105dvb->fe = dvb_attach( | ||
645 | si21xx_attach, &serit_config, | ||
646 | &dm1105dvb->i2c_adap); | ||
647 | if (dm1105dvb->fe) | ||
648 | dm1105dvb->fe->ops.set_voltage = | ||
649 | dm1105dvb_set_voltage; | ||
650 | } | ||
651 | #endif /* keep */ | ||
652 | break; | ||
653 | case PCI_DEVICE_ID_DW2004: | ||
654 | #if 0 | ||
655 | dm1105dvb->fe = dvb_attach( | ||
656 | cx24116_attach, &serit_sp2633_config, | ||
657 | &dm1105dvb->i2c_adap); | ||
658 | if (dm1105dvb->fe) | ||
659 | dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; | ||
660 | #else /* keep */ | ||
661 | dev_err(&dm1105dvb->pdev->dev, "needs cx24116 module\n"); | ||
662 | #endif /* keep */ | ||
663 | break; | ||
664 | } | ||
665 | |||
666 | if (!dm1105dvb->fe) { | ||
667 | dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n"); | ||
668 | return -ENODEV; | ||
669 | } | ||
670 | |||
671 | ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe); | ||
672 | if (ret < 0) { | ||
673 | if (dm1105dvb->fe->ops.release) | ||
674 | dm1105dvb->fe->ops.release(dm1105dvb->fe); | ||
675 | dm1105dvb->fe = NULL; | ||
676 | return ret; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac) | ||
683 | { | ||
684 | static u8 command[1] = { 0x28 }; | ||
685 | |||
686 | struct i2c_msg msg[] = { | ||
687 | { .addr = IIC_24C01_addr >> 1, .flags = 0, | ||
688 | .buf = command, .len = 1 }, | ||
689 | { .addr = IIC_24C01_addr >> 1, .flags = I2C_M_RD, | ||
690 | .buf = mac, .len = 6 }, | ||
691 | }; | ||
692 | |||
693 | dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2); | ||
694 | dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
695 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||
696 | } | ||
697 | |||
698 | static int __devinit dm1105_probe(struct pci_dev *pdev, | ||
699 | const struct pci_device_id *ent) | ||
700 | { | ||
701 | struct dm1105dvb *dm1105dvb; | ||
702 | struct dvb_adapter *dvb_adapter; | ||
703 | struct dvb_demux *dvbdemux; | ||
704 | struct dmx_demux *dmx; | ||
705 | int ret = -ENOMEM; | ||
706 | |||
707 | dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL); | ||
708 | if (!dm1105dvb) | ||
709 | goto out; | ||
710 | |||
711 | dm1105dvb->pdev = pdev; | ||
712 | dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES; | ||
713 | dm1105dvb->PacketErrorCount = 0; | ||
714 | dm1105dvb->dmarst = 0; | ||
715 | |||
716 | ret = pci_enable_device(pdev); | ||
717 | if (ret < 0) | ||
718 | goto err_kfree; | ||
719 | |||
720 | ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); | ||
721 | if (ret < 0) | ||
722 | goto err_pci_disable_device; | ||
723 | |||
724 | pci_set_master(pdev); | ||
725 | |||
726 | ret = pci_request_regions(pdev, DRIVER_NAME); | ||
727 | if (ret < 0) | ||
728 | goto err_pci_disable_device; | ||
729 | |||
730 | dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); | ||
731 | if (!dm1105dvb->io_mem) { | ||
732 | ret = -EIO; | ||
733 | goto err_pci_release_regions; | ||
734 | } | ||
735 | |||
736 | spin_lock_init(&dm1105dvb->lock); | ||
737 | pci_set_drvdata(pdev, dm1105dvb); | ||
738 | |||
739 | ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb); | ||
740 | if (ret < 0) | ||
741 | goto err_pci_iounmap; | ||
742 | |||
743 | ret = dm1105dvb_hw_init(dm1105dvb); | ||
744 | if (ret < 0) | ||
745 | goto err_free_irq; | ||
746 | |||
747 | /* i2c */ | ||
748 | i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb); | ||
749 | strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME); | ||
750 | dm1105dvb->i2c_adap.owner = THIS_MODULE; | ||
751 | dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL; | ||
752 | dm1105dvb->i2c_adap.dev.parent = &pdev->dev; | ||
753 | dm1105dvb->i2c_adap.algo = &dm1105_algo; | ||
754 | dm1105dvb->i2c_adap.algo_data = dm1105dvb; | ||
755 | ret = i2c_add_adapter(&dm1105dvb->i2c_adap); | ||
756 | |||
757 | if (ret < 0) | ||
758 | goto err_dm1105dvb_hw_exit; | ||
759 | |||
760 | /* dvb */ | ||
761 | ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME, | ||
762 | THIS_MODULE, &pdev->dev, adapter_nr); | ||
763 | if (ret < 0) | ||
764 | goto err_i2c_del_adapter; | ||
765 | |||
766 | dvb_adapter = &dm1105dvb->dvb_adapter; | ||
767 | |||
768 | dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac); | ||
769 | |||
770 | dvbdemux = &dm1105dvb->demux; | ||
771 | dvbdemux->filternum = 256; | ||
772 | dvbdemux->feednum = 256; | ||
773 | dvbdemux->start_feed = dm1105dvb_start_feed; | ||
774 | dvbdemux->stop_feed = dm1105dvb_stop_feed; | ||
775 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | ||
776 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | ||
777 | ret = dvb_dmx_init(dvbdemux); | ||
778 | if (ret < 0) | ||
779 | goto err_dvb_unregister_adapter; | ||
780 | |||
781 | dmx = &dvbdemux->dmx; | ||
782 | dm1105dvb->dmxdev.filternum = 256; | ||
783 | dm1105dvb->dmxdev.demux = dmx; | ||
784 | dm1105dvb->dmxdev.capabilities = 0; | ||
785 | |||
786 | ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter); | ||
787 | if (ret < 0) | ||
788 | goto err_dvb_dmx_release; | ||
789 | |||
790 | dm1105dvb->hw_frontend.source = DMX_FRONTEND_0; | ||
791 | |||
792 | ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend); | ||
793 | if (ret < 0) | ||
794 | goto err_dvb_dmxdev_release; | ||
795 | |||
796 | dm1105dvb->mem_frontend.source = DMX_MEMORY_FE; | ||
797 | |||
798 | ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend); | ||
799 | if (ret < 0) | ||
800 | goto err_remove_hw_frontend; | ||
801 | |||
802 | ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend); | ||
803 | if (ret < 0) | ||
804 | goto err_remove_mem_frontend; | ||
805 | |||
806 | ret = frontend_init(dm1105dvb); | ||
807 | if (ret < 0) | ||
808 | goto err_disconnect_frontend; | ||
809 | |||
810 | dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx); | ||
811 | dm1105_ir_init(dm1105dvb); | ||
812 | out: | ||
813 | return ret; | ||
814 | |||
815 | err_disconnect_frontend: | ||
816 | dmx->disconnect_frontend(dmx); | ||
817 | err_remove_mem_frontend: | ||
818 | dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); | ||
819 | err_remove_hw_frontend: | ||
820 | dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); | ||
821 | err_dvb_dmxdev_release: | ||
822 | dvb_dmxdev_release(&dm1105dvb->dmxdev); | ||
823 | err_dvb_dmx_release: | ||
824 | dvb_dmx_release(dvbdemux); | ||
825 | err_dvb_unregister_adapter: | ||
826 | dvb_unregister_adapter(dvb_adapter); | ||
827 | err_i2c_del_adapter: | ||
828 | i2c_del_adapter(&dm1105dvb->i2c_adap); | ||
829 | err_dm1105dvb_hw_exit: | ||
830 | dm1105dvb_hw_exit(dm1105dvb); | ||
831 | err_free_irq: | ||
832 | free_irq(pdev->irq, dm1105dvb); | ||
833 | err_pci_iounmap: | ||
834 | pci_iounmap(pdev, dm1105dvb->io_mem); | ||
835 | err_pci_release_regions: | ||
836 | pci_release_regions(pdev); | ||
837 | err_pci_disable_device: | ||
838 | pci_disable_device(pdev); | ||
839 | err_kfree: | ||
840 | pci_set_drvdata(pdev, NULL); | ||
841 | kfree(dm1105dvb); | ||
842 | goto out; | ||
843 | } | ||
844 | |||
845 | static void __devexit dm1105_remove(struct pci_dev *pdev) | ||
846 | { | ||
847 | struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev); | ||
848 | struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter; | ||
849 | struct dvb_demux *dvbdemux = &dm1105dvb->demux; | ||
850 | struct dmx_demux *dmx = &dvbdemux->dmx; | ||
851 | |||
852 | dm1105_ir_exit(dm1105dvb); | ||
853 | dmx->close(dmx); | ||
854 | dvb_net_release(&dm1105dvb->dvbnet); | ||
855 | if (dm1105dvb->fe) | ||
856 | dvb_unregister_frontend(dm1105dvb->fe); | ||
857 | |||
858 | dmx->disconnect_frontend(dmx); | ||
859 | dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); | ||
860 | dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); | ||
861 | dvb_dmxdev_release(&dm1105dvb->dmxdev); | ||
862 | dvb_dmx_release(dvbdemux); | ||
863 | dvb_unregister_adapter(dvb_adapter); | ||
864 | if (&dm1105dvb->i2c_adap) | ||
865 | i2c_del_adapter(&dm1105dvb->i2c_adap); | ||
866 | |||
867 | dm1105dvb_hw_exit(dm1105dvb); | ||
868 | synchronize_irq(pdev->irq); | ||
869 | free_irq(pdev->irq, dm1105dvb); | ||
870 | pci_iounmap(pdev, dm1105dvb->io_mem); | ||
871 | pci_release_regions(pdev); | ||
872 | pci_disable_device(pdev); | ||
873 | pci_set_drvdata(pdev, NULL); | ||
874 | kfree(dm1105dvb); | ||
875 | } | ||
876 | |||
877 | static struct pci_device_id dm1105_id_table[] __devinitdata = { | ||
878 | { | ||
879 | .vendor = PCI_VENDOR_ID_TRIGEM, | ||
880 | .device = PCI_DEVICE_ID_DM1105, | ||
881 | .subvendor = PCI_ANY_ID, | ||
882 | .subdevice = PCI_DEVICE_ID_DW2002, | ||
883 | }, { | ||
884 | .vendor = PCI_VENDOR_ID_TRIGEM, | ||
885 | .device = PCI_DEVICE_ID_DM1105, | ||
886 | .subvendor = PCI_ANY_ID, | ||
887 | .subdevice = PCI_DEVICE_ID_DW2004, | ||
888 | }, { | ||
889 | /* empty */ | ||
890 | }, | ||
891 | }; | ||
892 | |||
893 | MODULE_DEVICE_TABLE(pci, dm1105_id_table); | ||
894 | |||
895 | static struct pci_driver dm1105_driver = { | ||
896 | .name = DRIVER_NAME, | ||
897 | .id_table = dm1105_id_table, | ||
898 | .probe = dm1105_probe, | ||
899 | .remove = __devexit_p(dm1105_remove), | ||
900 | }; | ||
901 | |||
902 | static int __init dm1105_init(void) | ||
903 | { | ||
904 | return pci_register_driver(&dm1105_driver); | ||
905 | } | ||
906 | |||
907 | static void __exit dm1105_exit(void) | ||
908 | { | ||
909 | pci_unregister_driver(&dm1105_driver); | ||
910 | } | ||
911 | |||
912 | module_init(dm1105_init); | ||
913 | module_exit(dm1105_exit); | ||
914 | |||
915 | MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>"); | ||
916 | MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); | ||
917 | MODULE_LICENSE("GPL"); | ||