Rev 773 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
425 | giacomo | 1 | /* |
2 | saa7146.o - driver for generic saa7146-based hardware |
||
3 | |||
4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> |
||
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 | #include <media/saa7146.h> |
||
22 | |||
23 | /* global variables */ |
||
24 | struct list_head saa7146_devices; |
||
25 | struct semaphore saa7146_devices_lock; |
||
26 | |||
27 | static int initialized = 0; |
||
28 | int saa7146_num = 0; |
||
29 | |||
30 | unsigned int saa7146_debug = 0; |
||
31 | |||
32 | MODULE_PARM(saa7146_debug,"i"); |
||
33 | MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); |
||
34 | |||
35 | #if 0 |
||
36 | static void dump_registers(struct saa7146_dev* dev) |
||
37 | { |
||
38 | int i = 0; |
||
39 | |||
40 | INFO((" @ %li jiffies:\n",jiffies)); |
||
41 | for(i = 0; i <= 0x148; i+=4) { |
||
42 | printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i)); |
||
43 | } |
||
44 | } |
||
45 | #endif |
||
46 | |||
47 | /**************************************************************************** |
||
48 | * gpio and debi helper functions |
||
49 | ****************************************************************************/ |
||
50 | |||
51 | /* write "data" to the gpio-pin "pin" */ |
||
52 | void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data) |
||
53 | { |
||
54 | u32 value = 0; |
||
55 | |||
56 | /* sanity check */ |
||
57 | if(pin > 3) |
||
58 | return; |
||
59 | |||
60 | /* read old register contents */ |
||
61 | value = saa7146_read(dev, GPIO_CTRL ); |
||
62 | |||
63 | value &= ~(0xff << (8*pin)); |
||
64 | value |= (data << (8*pin)); |
||
65 | |||
66 | saa7146_write(dev, GPIO_CTRL, value); |
||
67 | } |
||
68 | |||
69 | /* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ |
||
70 | int saa7146_wait_for_debi_done(struct saa7146_dev *dev) |
||
71 | { |
||
72 | int start; |
||
73 | |||
74 | /* wait for registers to be programmed */ |
||
75 | start = jiffies; |
||
76 | while (1) { |
||
77 | if (saa7146_read(dev, MC2) & 2) |
||
78 | break; |
||
79 | if (jiffies-start > HZ/20) { |
||
80 | DEB_S(("timed out while waiting for registers getting programmed\n")); |
||
81 | return -ETIMEDOUT; |
||
82 | } |
||
83 | } |
||
84 | |||
85 | /* wait for transfer to complete */ |
||
86 | start = jiffies; |
||
87 | while (1) { |
||
88 | if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) |
||
89 | break; |
||
90 | saa7146_read(dev, MC2); |
||
91 | if (jiffies-start > HZ/4) { |
||
92 | DEB_S(("timed out while waiting for transfer completion\n")); |
||
93 | return -ETIMEDOUT; |
||
94 | } |
||
95 | } |
||
96 | |||
97 | return 0; |
||
98 | } |
||
99 | |||
100 | /**************************************************************************** |
||
101 | * general helper functions |
||
102 | ****************************************************************************/ |
||
103 | |||
104 | /* this is videobuf_vmalloc_to_sg() from video-buf.c |
||
105 | make sure virt has been allocated with vmalloc_32(), otherwise the BUG() |
||
106 | may be triggered on highmem machines */ |
||
107 | static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) |
||
108 | { |
||
109 | struct scatterlist *sglist; |
||
110 | struct page *pg; |
||
111 | int i; |
||
112 | |||
113 | sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); |
||
114 | if (NULL == sglist) |
||
115 | return NULL; |
||
116 | memset(sglist,0,sizeof(struct scatterlist)*nr_pages); |
||
117 | for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { |
||
118 | pg = vmalloc_to_page(virt); |
||
119 | if (NULL == pg) |
||
120 | goto err; |
||
121 | if (PageHighMem(pg)) |
||
122 | BUG(); |
||
123 | sglist[i].page = pg; |
||
124 | sglist[i].length = PAGE_SIZE; |
||
125 | } |
||
126 | return sglist; |
||
127 | |||
128 | err: |
||
129 | kfree(sglist); |
||
130 | return NULL; |
||
131 | } |
||
132 | |||
133 | /********************************************************************************/ |
||
134 | /* common page table functions */ |
||
135 | |||
136 | #define SAA7146_PGTABLE_SIZE 4096 |
||
137 | |||
138 | char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) |
||
139 | { |
||
140 | int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; |
||
141 | char *mem = vmalloc_32(length); |
||
142 | int slen = 0; |
||
143 | |||
144 | if (NULL == mem) { |
||
145 | return NULL; |
||
146 | } |
||
147 | |||
148 | if (!(pt->slist = vmalloc_to_sg(mem, pages))) { |
||
149 | vfree(mem); |
||
150 | return NULL; |
||
151 | } |
||
152 | |||
153 | if (saa7146_pgtable_alloc(pci, pt)) { |
||
154 | kfree(pt->slist); |
||
155 | pt->slist = NULL; |
||
156 | vfree(mem); |
||
157 | return NULL; |
||
158 | } |
||
159 | |||
160 | slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE); |
||
161 | if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) { |
||
162 | return NULL; |
||
163 | } |
||
164 | |||
165 | return mem; |
||
166 | } |
||
167 | |||
168 | void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) |
||
169 | { |
||
170 | if (NULL == pt->cpu) |
||
171 | return; |
||
172 | pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); |
||
173 | pt->cpu = NULL; |
||
174 | if (NULL != pt->slist) { |
||
175 | kfree(pt->slist); |
||
176 | pt->slist = NULL; |
||
177 | } |
||
178 | } |
||
179 | |||
180 | int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) |
||
181 | { |
||
182 | u32 *cpu; |
||
183 | dma_addr_t dma_addr; |
||
184 | |||
185 | cpu = pci_alloc_consistent(pci, SAA7146_PGTABLE_SIZE, &dma_addr); |
||
186 | if (NULL == cpu) { |
||
187 | return -ENOMEM; |
||
188 | } |
||
189 | pt->size = SAA7146_PGTABLE_SIZE; |
||
190 | pt->cpu = cpu; |
||
191 | pt->dma = dma_addr; |
||
192 | |||
193 | return 0; |
||
194 | } |
||
195 | |||
196 | int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, |
||
197 | struct scatterlist *list, int sglen ) |
||
198 | { |
||
199 | u32 *ptr, fill; |
||
200 | int nr_pages = 0; |
||
201 | int i,p; |
||
202 | |||
203 | BUG_ON( 0 == sglen); |
||
204 | |||
205 | if (list->offset > PAGE_SIZE) { |
||
206 | DEB_D(("offset > PAGE_SIZE. this should not happen.")); |
||
207 | return -EINVAL; |
||
208 | } |
||
209 | |||
210 | /* if we have a user buffer, the first page may not be |
||
211 | aligned to a page boundary. */ |
||
212 | pt->offset = list->offset; |
||
213 | |||
214 | ptr = pt->cpu; |
||
215 | for (i = 0; i < sglen; i++, list++) { |
||
216 | /* |
||
217 | printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset); |
||
218 | */ |
||
219 | for (p = 0; p * 4096 < list->length; p++, ptr++) { |
||
220 | *ptr = sg_dma_address(list) + p * 4096; |
||
221 | nr_pages++; |
||
222 | } |
||
223 | } |
||
224 | |||
225 | |||
226 | /* safety; fill the page table up with the last valid page */ |
||
227 | fill = *(ptr-1); |
||
228 | for(i=nr_pages;i<1024;i++) { |
||
229 | *ptr++ = fill; |
||
230 | } |
||
231 | |||
232 | /* |
||
233 | ptr = pt->cpu; |
||
234 | printk("offset: %d\n",pt->offset); |
||
235 | for(i=0;i<5;i++) { |
||
236 | printk("ptr1 %d: 0x%08x\n",i,ptr[i]); |
||
237 | } |
||
238 | */ |
||
239 | return 0; |
||
240 | } |
||
241 | |||
242 | /********************************************************************************/ |
||
243 | /* gpio functions */ |
||
244 | |||
245 | void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) |
||
246 | { |
||
247 | u32 val = 0; |
||
248 | |||
249 | val=saa7146_read(dev,GPIO_CTRL); |
||
250 | val&=~(0xff << (8*(port))); |
||
251 | val|=(data)<<(8*(port)); |
||
252 | saa7146_write(dev, GPIO_CTRL, val); |
||
253 | } |
||
254 | |||
255 | /********************************************************************************/ |
||
256 | /* interrupt handler */ |
||
257 | static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) |
||
258 | { |
||
259 | struct saa7146_dev *dev = (struct saa7146_dev*)dev_id; |
||
260 | u32 isr = 0; |
||
261 | |||
262 | /* read out the interrupt status register */ |
||
263 | isr = saa7146_read(dev, ISR); |
||
264 | |||
265 | /* is this our interrupt? */ |
||
266 | if ( 0 == isr ) { |
||
267 | /* nope, some other device */ |
||
268 | return IRQ_NONE; |
||
269 | } |
||
270 | |||
271 | saa7146_write(dev, ISR, isr); |
||
272 | |||
273 | if( 0 != (dev->ext)) { |
||
274 | if( 0 != (dev->ext->irq_mask & isr )) { |
||
275 | if( 0 != dev->ext->irq_func ) { |
||
276 | dev->ext->irq_func(dev, &isr); |
||
277 | } |
||
278 | isr &= ~dev->ext->irq_mask; |
||
279 | } |
||
280 | } |
||
281 | if (0 != (isr & (MASK_27))) { |
||
282 | DEB_INT(("irq: RPS0 (0x%08x).\n",isr)); |
||
283 | if( 0 != dev->vv_data && 0 != dev->vv_callback) { |
||
284 | dev->vv_callback(dev,isr); |
||
285 | } |
||
286 | isr &= ~MASK_27; |
||
287 | } |
||
288 | if (0 != (isr & (MASK_28))) { |
||
289 | if( 0 != dev->vv_data && 0 != dev->vv_callback) { |
||
290 | dev->vv_callback(dev,isr); |
||
291 | } |
||
292 | isr &= ~MASK_28; |
||
293 | } |
||
294 | if (0 != (isr & (MASK_16|MASK_17))) { |
||
295 | u32 status = saa7146_read(dev, I2C_STATUS); |
||
296 | if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) { |
||
297 | IER_DISABLE(dev, MASK_16|MASK_17); |
||
298 | /* only wake up if we expect something */ |
||
299 | if( 0 != dev->i2c_op ) { |
||
300 | u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2; |
||
301 | u32 ssr = (saa7146_read(dev, SSR) >> 17) & 0x1f; |
||
302 | DEB_I2C(("irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).\n",status,psr,ssr)); |
||
303 | dev->i2c_op = 0; |
||
304 | wake_up(&dev->i2c_wq); |
||
305 | } else { |
||
306 | DEB_I2C(("unexpected irq: i2c, status: 0x%08x, isr %#x\n",status, isr)); |
||
307 | } |
||
308 | } else { |
||
309 | DEB_I2C(("unhandled irq: i2c, status: 0x%08x, isr %#x\n",status, isr)); |
||
310 | } |
||
311 | isr &= ~(MASK_16|MASK_17); |
||
312 | } |
||
313 | if( 0 != isr ) { |
||
314 | ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr)); |
||
315 | ERR(("disabling interrupt source(s)!\n")); |
||
316 | IER_DISABLE(dev,isr); |
||
317 | } |
||
318 | return IRQ_HANDLED; |
||
319 | } |
||
320 | |||
321 | /*********************************************************************************/ |
||
322 | /* configuration-functions */ |
||
323 | |||
324 | static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) |
||
325 | { |
||
326 | unsigned long adr = 0, len = 0; |
||
327 | struct saa7146_dev* dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL); |
||
328 | |||
329 | struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; |
||
330 | struct saa7146_extension* ext = pci_ext->ext; |
||
331 | int err = 0; |
||
332 | |||
333 | if (!(dev = kmalloc (sizeof(struct saa7146_dev),GFP_KERNEL))) { |
||
334 | ERR(("out of memory.\n")); |
||
335 | return -ENOMEM; |
||
336 | } |
||
337 | |||
338 | /* clear out mem for sure */ |
||
339 | memset(dev, 0x0, sizeof(struct saa7146_dev)); |
||
340 | |||
341 | DEB_EE(("pci:%p\n",pci)); |
||
342 | |||
343 | if (pci_enable_device(pci)) { |
||
344 | ERR(("pci_enable_device() failed.\n")); |
||
345 | err = -EIO; |
||
346 | goto pci_error; |
||
347 | } |
||
348 | |||
349 | /* enable bus-mastering */ |
||
350 | pci_set_master(pci); |
||
351 | |||
352 | dev->pci = pci; |
||
353 | /* get chip-revision; this is needed to enable bug-fixes */ |
||
354 | if( 0 > pci_read_config_dword(dev->pci, PCI_CLASS_REVISION, &dev->revision)) { |
||
355 | ERR(("pci_read_config_dword() failed.\n")); |
||
356 | err = -ENODEV; |
||
357 | goto pci_error; |
||
358 | } |
||
359 | dev->revision &= 0xf; |
||
360 | |||
361 | /* remap the memory from virtual to physical adress */ |
||
362 | adr = pci_resource_start(pci,0); |
||
363 | len = pci_resource_len(pci,0); |
||
364 | |||
365 | if (!request_mem_region(pci_resource_start(pci,0), pci_resource_len(pci,0), "saa7146")) { |
||
366 | ERR(("request_mem_region() failed.\n")); |
||
367 | err = -ENODEV; |
||
368 | goto pci_error; |
||
369 | } |
||
370 | |||
371 | if (!(dev->mem = ioremap(adr,len))) { |
||
372 | ERR(("ioremap() failed.\n")); |
||
373 | err = -ENODEV; |
||
374 | goto ioremap_error; |
||
375 | } |
||
376 | |||
377 | /* we don't do a master reset here anymore, it screws up |
||
378 | some boards that don't have an i2c-eeprom for configuration |
||
379 | values */ |
||
380 | /* |
||
381 | saa7146_write(dev, MC1, MASK_31); |
||
382 | */ |
||
383 | |||
384 | /* disable all irqs */ |
||
385 | saa7146_write(dev, IER, 0); |
||
386 | |||
387 | /* shut down all dma transfers */ |
||
388 | saa7146_write(dev, MC1, 0x00ff0000); |
||
389 | |||
390 | /* clear out any rps-signals pending */ |
||
391 | saa7146_write(dev, MC2, 0xf8000000); |
||
392 | |||
393 | /* request an interrupt for the saa7146 */ |
||
394 | if (request_irq(dev->pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT, |
||
395 | dev->name, dev)) |
||
396 | { |
||
397 | ERR(("request_irq() failed.\n")); |
||
398 | err = -ENODEV; |
||
399 | goto irq_error; |
||
400 | } |
||
401 | |||
402 | /* get memory for various stuff */ |
||
403 | dev->d_rps0.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps0.dma_handle); |
||
404 | if( NULL == dev->d_rps0.cpu_addr ) { |
||
405 | err = -ENOMEM; |
||
406 | goto kmalloc_error_1; |
||
407 | } |
||
408 | memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM); |
||
409 | |||
410 | dev->d_rps1.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_rps1.dma_handle); |
||
411 | if( NULL == dev->d_rps1.cpu_addr ) { |
||
412 | err = -ENOMEM; |
||
413 | goto kmalloc_error_2; |
||
414 | } |
||
415 | memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM); |
||
416 | |||
417 | dev->d_i2c.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_RPS_MEM, &dev->d_i2c.dma_handle); |
||
418 | if( NULL == dev->d_i2c.cpu_addr ) { |
||
419 | err = -ENOMEM; |
||
420 | goto kmalloc_error_3; |
||
421 | } |
||
422 | memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM); |
||
423 | |||
424 | /* the rest + print status message */ |
||
425 | |||
426 | /* create a nice device name */ |
||
427 | sprintf(&dev->name[0], "saa7146 (%d)",saa7146_num); |
||
428 | |||
429 | INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision,dev->pci->irq,dev->pci->subsystem_vendor,dev->pci->subsystem_device)); |
||
430 | dev->ext = ext; |
||
431 | |||
432 | pci_set_drvdata(pci,dev); |
||
433 | |||
434 | init_MUTEX(&dev->lock); |
||
435 | dev->int_slock = SPIN_LOCK_UNLOCKED; |
||
436 | dev->slock = SPIN_LOCK_UNLOCKED; |
||
437 | |||
438 | init_MUTEX(&dev->i2c_lock); |
||
439 | |||
440 | dev->module = THIS_MODULE; |
||
441 | init_waitqueue_head(&dev->i2c_wq); |
||
442 | |||
443 | /* set some sane pci arbitrition values */ |
||
444 | saa7146_write(dev, PCI_BT_V1, 0x1c00101f); |
||
445 | |||
446 | if( 0 != ext->probe) { |
||
447 | if( 0 != ext->probe(dev) ) { |
||
448 | DEB_D(("ext->probe() failed for %p. skipping device.\n",dev)); |
||
449 | err = -ENODEV; |
||
450 | goto probe_error; |
||
451 | } |
||
452 | } |
||
453 | |||
454 | if( 0 != ext->attach(dev,pci_ext) ) { |
||
455 | DEB_D(("ext->attach() failed for %p. skipping device.\n",dev)); |
||
456 | err = -ENODEV; |
||
457 | goto attach_error; |
||
458 | } |
||
459 | |||
460 | INIT_LIST_HEAD(&dev->item); |
||
461 | list_add_tail(&dev->item,&saa7146_devices); |
||
462 | saa7146_num++; |
||
463 | |||
464 | err = 0; |
||
465 | goto out; |
||
466 | attach_error: |
||
467 | probe_error: |
||
468 | pci_set_drvdata(pci,NULL); |
||
469 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle); |
||
470 | kmalloc_error_3: |
||
471 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle); |
||
472 | kmalloc_error_2: |
||
473 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle); |
||
474 | kmalloc_error_1: |
||
475 | free_irq(dev->pci->irq, (void *)dev); |
||
476 | irq_error: |
||
477 | iounmap(dev->mem); |
||
478 | ioremap_error: |
||
479 | release_mem_region(adr,len); |
||
480 | pci_error: |
||
481 | kfree(dev); |
||
482 | out: |
||
483 | return err; |
||
484 | } |
||
485 | |||
486 | static void saa7146_remove_one(struct pci_dev *pdev) |
||
487 | { |
||
488 | struct saa7146_dev* dev = (struct saa7146_dev*) pci_get_drvdata(pdev); |
||
489 | DEB_EE(("dev:%p\n",dev)); |
||
490 | |||
491 | dev->ext->detach(dev); |
||
492 | |||
493 | /* shut down all video dma transfers */ |
||
494 | saa7146_write(dev, MC1, 0x00ff0000); |
||
495 | |||
496 | /* disable all irqs, release irq-routine */ |
||
497 | saa7146_write(dev, IER, 0); |
||
498 | |||
499 | free_irq(dev->pci->irq, (void *)dev); |
||
500 | |||
501 | /* free kernel memory */ |
||
502 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle); |
||
503 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle); |
||
504 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle); |
||
505 | |||
506 | iounmap(dev->mem); |
||
507 | release_mem_region(pci_resource_start(dev->pci,0), pci_resource_len(dev->pci,0)); |
||
508 | |||
509 | list_del(&dev->item); |
||
510 | kfree(dev); |
||
511 | |||
512 | saa7146_num--; |
||
513 | } |
||
514 | |||
515 | /*********************************************************************************/ |
||
516 | /* extension handling functions */ |
||
517 | |||
518 | int saa7146_register_extension(struct saa7146_extension* ext) |
||
519 | { |
||
520 | DEB_EE(("ext:%p\n",ext)); |
||
521 | |||
522 | if( 0 == initialized ) { |
||
523 | INIT_LIST_HEAD(&saa7146_devices); |
||
524 | init_MUTEX(&saa7146_devices_lock); |
||
525 | initialized = 1; |
||
526 | } |
||
527 | |||
528 | ext->driver.name = ext->name; |
||
529 | ext->driver.id_table = ext->pci_tbl; |
||
530 | ext->driver.probe = saa7146_init_one; |
||
531 | ext->driver.remove = saa7146_remove_one; |
||
532 | |||
533 | printk("saa7146: register extension '%s'.\n",ext->name); |
||
534 | return pci_module_init(&ext->driver); |
||
535 | } |
||
536 | |||
537 | int saa7146_unregister_extension(struct saa7146_extension* ext) |
||
538 | { |
||
539 | DEB_EE(("ext:%p\n",ext)); |
||
540 | printk("saa7146: unregister extension '%s'.\n",ext->name); |
||
541 | pci_unregister_driver(&ext->driver); |
||
542 | return 0; |
||
543 | } |
||
544 | |||
545 | static int __init saa7146_init_module(void) |
||
546 | { |
||
547 | if( 0 == initialized ) { |
||
548 | INIT_LIST_HEAD(&saa7146_devices); |
||
549 | init_MUTEX(&saa7146_devices_lock); |
||
550 | initialized = 1; |
||
551 | } |
||
552 | return 0; |
||
553 | } |
||
554 | |||
555 | static void __exit saa7146_cleanup_module(void) |
||
556 | { |
||
557 | } |
||
558 | |||
559 | module_init(saa7146_init_module); |
||
560 | module_exit(saa7146_cleanup_module); |
||
561 | |||
562 | EXPORT_SYMBOL_GPL(saa7146_register_extension); |
||
563 | EXPORT_SYMBOL_GPL(saa7146_unregister_extension); |
||
564 | |||
565 | /* misc functions used by extension modules */ |
||
566 | EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); |
||
567 | EXPORT_SYMBOL_GPL(saa7146_pgtable_free); |
||
568 | EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); |
||
569 | EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); |
||
570 | EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); |
||
571 | |||
572 | EXPORT_SYMBOL_GPL(saa7146_setgpio); |
||
573 | |||
574 | EXPORT_SYMBOL_GPL(saa7146_i2c_transfer); |
||
575 | EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); |
||
576 | |||
577 | EXPORT_SYMBOL_GPL(saa7146_debug); |
||
578 | EXPORT_SYMBOL_GPL(saa7146_devices); |
||
579 | EXPORT_SYMBOL_GPL(saa7146_devices_lock); |
||
580 | |||
581 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); |
||
582 | MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); |
||
583 | MODULE_LICENSE("GPL"); |