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