]>
Commit | Line | Data |
---|---|---|
aa337ef1 SS |
1 | /* |
2 | ||
3 | Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, | |
4 | Jason Lapenta, Scott Smedley, Greg Sharp | |
5 | ||
6 | This file is part of the DT3155 Device Driver. | |
7 | ||
8 | The DT3155 Device Driver is free software; you can redistribute it | |
9 | and/or modify it under the terms of the GNU General Public License as | |
10 | published by the Free Software Foundation; either version 2 of the | |
11 | License, or (at your option) any later version. | |
12 | ||
13 | The DT3155 Device Driver is distributed in the hope that it will be | |
14 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with the DT3155 Device Driver; if not, write to the Free | |
20 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | MA 02111-1307 USA | |
22 | ||
23 | -- Changes -- | |
24 | ||
25 | Date Programmer Description of changes made | |
26 | ------------------------------------------------------------------- | |
27 | 03-Jul-2000 JML n/a | |
28 | 10-Oct-2001 SS port to 2.4 kernel | |
29 | 02-Apr-2002 SS Mods to use allocator as a standalone module; | |
30 | Merged John Roll's changes (john@cfa.harvard.edu) | |
31 | to make work with multiple boards. | |
32 | 02-Jul-2002 SS Merged James Rose's chages (rosejr@purdue.edu) to: | |
33 | * fix successive interrupt-driven captures | |
34 | * add select/poll support. | |
35 | 10-Jul-2002 GCS Add error check when ndevices > MAXBOARDS. | |
36 | 02-Aug-2002 GCS Fix field mode so that odd (lower) field is stored | |
37 | in lower half of buffer. | |
38 | 05-Aug-2005 SS port to 2.6 kernel. | |
39 | 26-Oct-2009 SS port to 2.6.30 kernel. | |
40 | ||
41 | -- Notes -- | |
42 | ||
43 | ** appended "mem=124" in lilo.conf to allow for 4megs free on my 128meg system. | |
44 | * using allocator.c and allocator.h from o'reilly book (alessandro rubini) | |
45 | ftp://ftp.systemy.it/pub/develop (see README.allocator) | |
46 | ||
47 | + might want to get rid of MAXboards for allocating initial buffer. | |
48 | confusing and not necessary | |
49 | ||
50 | + in cleanup_module the MOD_IN_USE looks like it is check after it should | |
51 | ||
52 | * GFP_DMA should not be set with a PCI system (pg 291) | |
53 | ||
54 | - NJC why are only two buffers allowed? (see isr, approx line 358) | |
55 | ||
56 | */ | |
57 | ||
aa337ef1 | 58 | #include <linux/module.h> |
aa337ef1 | 59 | #include <linux/interrupt.h> |
8e2394a9 | 60 | #include <linux/mutex.h> |
aa337ef1 SS |
61 | #include <linux/pci.h> |
62 | #include <linux/types.h> | |
63 | #include <linux/poll.h> | |
ae7fd7b8 | 64 | #include <linux/sched.h> |
b1f2ac07 | 65 | #include <linux/smp_lock.h> |
55bb6ece | 66 | #include <linux/io.h> |
aa337ef1 | 67 | |
aa337ef1 SS |
68 | #include <asm/uaccess.h> |
69 | ||
70 | #include "dt3155.h" | |
71 | #include "dt3155_drv.h" | |
72 | #include "dt3155_isr.h" | |
73 | #include "dt3155_io.h" | |
74 | #include "allocator.h" | |
75 | ||
74a92013 RD |
76 | |
77 | MODULE_LICENSE("GPL"); | |
78 | ||
aa337ef1 | 79 | /* Error variable. Zero means no error. */ |
8e2394a9 | 80 | static DEFINE_MUTEX(dt3155_mutex); |
aa337ef1 SS |
81 | int dt3155_errno = 0; |
82 | ||
83 | #ifndef PCI_DEVICE_ID_INTEL_7116 | |
84 | #define PCI_DEVICE_ID_INTEL_7116 0x1223 | |
85 | #endif | |
86 | ||
87 | #define DT3155_VENDORID PCI_VENDOR_ID_INTEL | |
88 | #define DT3155_DEVICEID PCI_DEVICE_ID_INTEL_7116 | |
89 | #define MAXPCI 16 | |
90 | ||
91 | #ifdef DT_DEBUG | |
92 | #define DT_3155_DEBUG_MSG(x,y) printk(x,y) | |
93 | #else | |
94 | #define DT_3155_DEBUG_MSG(x,y) | |
95 | #endif | |
96 | ||
97 | /* wait queue for interrupts */ | |
d241fd58 | 98 | wait_queue_head_t dt3155_read_wait_queue[MAXBOARDS]; |
aa337ef1 | 99 | |
aa337ef1 SS |
100 | /* set to dynamicaly allocate, but it is tunable: */ |
101 | /* insmod DT_3155 dt3155 dt3155_major=XX */ | |
102 | int dt3155_major = 0; | |
103 | ||
104 | /* The minor numbers are 0 and 1 ... they are not tunable. | |
105 | * They are used as the indices for the structure vectors, | |
106 | * and register address vectors | |
107 | */ | |
108 | ||
109 | /* Global structures and variables */ | |
110 | ||
111 | /* Status of each device */ | |
923c1244 | 112 | struct dt3155_status dt3155_status[MAXBOARDS]; |
aa337ef1 SS |
113 | |
114 | /* kernel logical address of the board */ | |
55bb6ece | 115 | static void __iomem *dt3155_lbase[MAXBOARDS] = { NULL |
aa337ef1 SS |
116 | #if MAXBOARDS == 2 |
117 | , NULL | |
118 | #endif | |
119 | }; | |
55bb6ece | 120 | |
d241fd58 | 121 | u32 dt3155_dev_open[MAXBOARDS] = {0 |
aa337ef1 SS |
122 | #if MAXBOARDS == 2 |
123 | , 0 | |
124 | #endif | |
125 | }; | |
126 | ||
dcff74ce | 127 | u32 ndevices = 0; |
3a8954e8 | 128 | u32 unique_tag = 0;; |
aa337ef1 SS |
129 | |
130 | ||
131 | /* | |
132 | * Stops interrupt generation right away and resets the status | |
133 | * to idle. I don't know why this works and the other way doesn't. | |
134 | * (James Rose) | |
135 | */ | |
136 | static void quick_stop (int minor) | |
137 | { | |
7d0f940e HS |
138 | struct dt3155_status *dts = &dt3155_status[minor]; |
139 | struct dt3155_fbuffer *fb = &dts->fbuffer; | |
17701d14 | 140 | |
aa337ef1 SS |
141 | // TODO: scott was here |
142 | #if 1 | |
aadbdeb6 HS |
143 | INT_CSR_R int_csr_r; |
144 | ||
55bb6ece | 145 | int_csr_r.reg = readl(dt3155_lbase[minor] + INT_CSR); |
aa337ef1 SS |
146 | /* disable interrupts */ |
147 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
148 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
55bb6ece | 149 | writel(int_csr_r.reg, dt3155_lbase[minor] + INT_CSR); |
aa337ef1 | 150 | |
7d0f940e | 151 | dts->state &= ~(DT3155_STATE_STOP|0xff); |
aa337ef1 | 152 | /* mark the system stopped: */ |
7d0f940e | 153 | dts->state |= DT3155_STATE_IDLE; |
17701d14 HS |
154 | fb->stop_acquire = 0; |
155 | fb->even_stopped = 0; | |
aa337ef1 | 156 | #else |
7d0f940e | 157 | dts->state |= DT3155_STATE_STOP; |
17701d14 | 158 | fb->stop_acquire = 1; |
aa337ef1 SS |
159 | #endif |
160 | ||
161 | } | |
162 | ||
163 | ||
164 | /***************************************************** | |
165 | * dt3155_isr() Interrupt service routien | |
166 | * | |
167 | * - looks like this isr supports IRQ sharing (or could) JML | |
168 | * - Assumes irq's are disabled, via SA_INTERRUPT flag | |
169 | * being set in request_irq() call from init_module() | |
170 | *****************************************************/ | |
e8afd402 | 171 | static void dt3155_isr(int irq, void *dev_id, struct pt_regs *regs) |
aa337ef1 SS |
172 | { |
173 | int minor = -1; | |
174 | int index; | |
aa337ef1 | 175 | unsigned long flags; |
3a8954e8 | 176 | u32 buffer_addr; |
55bb6ece | 177 | void __iomem *mmio; |
7d0f940e | 178 | struct dt3155_status *dts; |
17701d14 | 179 | struct dt3155_fbuffer *fb; |
aadbdeb6 HS |
180 | INT_CSR_R int_csr_r; |
181 | CSR1_R csr1_r; | |
182 | I2C_EVEN_CSR i2c_even_csr; | |
183 | I2C_ODD_CSR i2c_odd_csr; | |
aa337ef1 SS |
184 | |
185 | /* find out who issued the interrupt */ | |
d241fd58 JB |
186 | for (index = 0; index < ndevices; index++) { |
187 | if(dev_id == (void*) &dt3155_status[index]) | |
aa337ef1 SS |
188 | { |
189 | minor = index; | |
190 | break; | |
191 | } | |
192 | } | |
193 | ||
194 | /* hopefully we should not get here */ | |
d241fd58 | 195 | if (minor < 0 || minor >= MAXBOARDS) { |
aa337ef1 SS |
196 | printk(KERN_ERR "dt3155_isr called with invalid dev_id\n"); |
197 | return; | |
198 | } | |
199 | ||
55bb6ece | 200 | mmio = dt3155_lbase[minor]; |
7d0f940e HS |
201 | dts = &dt3155_status[minor]; |
202 | fb = &dts->fbuffer; | |
55bb6ece | 203 | |
aa337ef1 | 204 | /* Check for corruption and set a flag if so */ |
55bb6ece | 205 | csr1_r.reg = readl(mmio + CSR1); |
aa337ef1 | 206 | |
d241fd58 | 207 | if ((csr1_r.fld.FLD_CRPT_EVE) || (csr1_r.fld.FLD_CRPT_ODD)) |
aa337ef1 SS |
208 | { |
209 | /* TODO: this should probably stop acquisition */ | |
210 | /* and set some flags so that dt3155_read */ | |
211 | /* returns an error next time it is called */ | |
212 | dt3155_errno = DT_ERR_CORRUPT; | |
213 | printk("dt3155: corrupt field\n"); | |
214 | return; | |
215 | } | |
216 | ||
55bb6ece | 217 | int_csr_r.reg = readl(mmio + INT_CSR); |
aa337ef1 SS |
218 | |
219 | /* Handle the even field ... */ | |
220 | if (int_csr_r.fld.FLD_END_EVE) | |
221 | { | |
7d0f940e | 222 | if ((dts->state & DT3155_STATE_MODE) == DT3155_STATE_FLD) |
aa337ef1 | 223 | { |
17701d14 | 224 | fb->frame_count++; |
aa337ef1 SS |
225 | } |
226 | ||
55bb6ece | 227 | ReadI2C(mmio, EVEN_CSR, &i2c_even_csr.reg); |
aa337ef1 SS |
228 | |
229 | /* Clear the interrupt? */ | |
230 | int_csr_r.fld.FLD_END_EVE = 1; | |
231 | ||
232 | /* disable the interrupt if last field */ | |
17701d14 | 233 | if (fb->stop_acquire) |
aa337ef1 SS |
234 | { |
235 | printk("dt3155: even stopped.\n"); | |
17701d14 | 236 | fb->even_stopped = 1; |
aa337ef1 SS |
237 | if (i2c_even_csr.fld.SNGL_EVE) |
238 | { | |
239 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
240 | } | |
241 | else | |
242 | { | |
243 | i2c_even_csr.fld.SNGL_EVE = 1; | |
244 | } | |
245 | } | |
246 | ||
55bb6ece | 247 | writel(int_csr_r.reg, mmio + INT_CSR); |
aa337ef1 SS |
248 | |
249 | /* Set up next DMA if we are doing FIELDS */ | |
7d0f940e | 250 | if ((dts->state & DT3155_STATE_MODE) == DT3155_STATE_FLD) |
aa337ef1 SS |
251 | { |
252 | /* GCS (Aug 2, 2002) -- In field mode, dma the odd field | |
253 | into the lower half of the buffer */ | |
7d0f940e | 254 | const u32 stride = dts->config.cols; |
17701d14 HS |
255 | buffer_addr = fb->frame_info[fb->active_buf].addr + |
256 | (DT3155_MAX_ROWS / 2) * stride; | |
aa337ef1 SS |
257 | local_save_flags(flags); |
258 | local_irq_disable(); | |
d241fd58 | 259 | wake_up_interruptible(&dt3155_read_wait_queue[minor]); |
aa337ef1 SS |
260 | |
261 | /* Set up the DMA address for the next field */ | |
aa337ef1 | 262 | local_irq_restore(flags); |
55bb6ece | 263 | writel(buffer_addr, mmio + ODD_DMA_START); |
aa337ef1 SS |
264 | } |
265 | ||
266 | /* Check for errors. */ | |
267 | i2c_even_csr.fld.DONE_EVE = 1; | |
d241fd58 | 268 | if (i2c_even_csr.fld.ERROR_EVE) |
aa337ef1 SS |
269 | dt3155_errno = DT_ERR_OVERRUN; |
270 | ||
55bb6ece | 271 | WriteI2C(mmio, EVEN_CSR, i2c_even_csr.reg); |
aa337ef1 SS |
272 | |
273 | /* Note that we actually saw an even field meaning */ | |
274 | /* that subsequent odd field complete the frame */ | |
17701d14 | 275 | fb->even_happened = 1; |
aa337ef1 SS |
276 | |
277 | /* recording the time that the even field finished, this should be */ | |
278 | /* about time in the middle of the frame */ | |
17701d14 | 279 | do_gettimeofday(&fb->frame_info[fb->active_buf].time); |
aa337ef1 SS |
280 | return; |
281 | } | |
282 | ||
283 | /* ... now handle the odd field */ | |
d241fd58 | 284 | if (int_csr_r.fld.FLD_END_ODD) |
aa337ef1 | 285 | { |
55bb6ece | 286 | ReadI2C(mmio, ODD_CSR, &i2c_odd_csr.reg); |
aa337ef1 SS |
287 | |
288 | /* Clear the interrupt? */ | |
289 | int_csr_r.fld.FLD_END_ODD = 1; | |
290 | ||
17701d14 | 291 | if (fb->even_happened || |
7d0f940e | 292 | (dts->state & DT3155_STATE_MODE) == DT3155_STATE_FLD) |
aa337ef1 | 293 | { |
17701d14 | 294 | fb->frame_count++; |
aa337ef1 SS |
295 | } |
296 | ||
17701d14 | 297 | if (fb->stop_acquire && fb->even_stopped) |
aa337ef1 SS |
298 | { |
299 | printk(KERN_DEBUG "dt3155: stopping odd..\n"); | |
d241fd58 | 300 | if (i2c_odd_csr.fld.SNGL_ODD) |
aa337ef1 SS |
301 | { |
302 | /* disable interrupts */ | |
303 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
7d0f940e | 304 | dts->state &= ~(DT3155_STATE_STOP|0xff); |
aa337ef1 SS |
305 | |
306 | /* mark the system stopped: */ | |
7d0f940e | 307 | dts->state |= DT3155_STATE_IDLE; |
17701d14 HS |
308 | fb->stop_acquire = 0; |
309 | fb->even_stopped = 0; | |
aa337ef1 | 310 | |
7d0f940e | 311 | printk(KERN_DEBUG "dt3155: state is now %x\n", dts->state); |
aa337ef1 SS |
312 | } |
313 | else | |
314 | { | |
315 | i2c_odd_csr.fld.SNGL_ODD = 1; | |
316 | } | |
317 | } | |
318 | ||
55bb6ece | 319 | writel(int_csr_r.reg, mmio + INT_CSR); |
aa337ef1 SS |
320 | |
321 | /* if the odd field has been acquired, then */ | |
322 | /* change the next dma location for both fields */ | |
323 | /* and wake up the process if sleeping */ | |
17701d14 | 324 | if (fb->even_happened || |
7d0f940e | 325 | (dts->state & DT3155_STATE_MODE) == DT3155_STATE_FLD) |
aa337ef1 SS |
326 | { |
327 | ||
aa337ef1 SS |
328 | local_save_flags(flags); |
329 | local_irq_disable(); | |
aa337ef1 SS |
330 | |
331 | #ifdef DEBUG_QUES_B | |
843894ad | 332 | printques(fb); |
aa337ef1 | 333 | #endif |
17701d14 | 334 | if (fb->nbuffers > 2) |
aa337ef1 | 335 | { |
843894ad | 336 | if (!are_empty_buffers(fb)) |
aa337ef1 SS |
337 | { |
338 | /* The number of active + locked buffers is | |
339 | * at most 2, and since there are none empty, there | |
340 | * must be at least nbuffers-2 ready buffers. | |
341 | * This is where we 'drop frames', oldest first. */ | |
843894ad | 342 | push_empty(fb, pop_ready(fb)); |
aa337ef1 SS |
343 | } |
344 | ||
345 | /* The ready_que can't be full, since we know | |
346 | * there is one active buffer right now, so it's safe | |
347 | * to push the active buf on the ready_que. */ | |
843894ad | 348 | push_ready(fb, fb->active_buf); |
aa337ef1 | 349 | /* There's at least 1 empty -- make it active */ |
843894ad | 350 | fb->active_buf = pop_empty(fb); |
17701d14 | 351 | fb->frame_info[fb->active_buf].tag = ++unique_tag; |
aa337ef1 SS |
352 | } |
353 | else /* nbuffers == 2, special case */ | |
354 | { /* There is 1 active buffer. | |
355 | * If there is a locked buffer, keep the active buffer | |
356 | * the same -- that means we drop a frame. | |
357 | */ | |
17701d14 | 358 | if (fb->locked_buf < 0) |
aa337ef1 | 359 | { |
843894ad HS |
360 | push_ready(fb, fb->active_buf); |
361 | if (are_empty_buffers(fb)) | |
aa337ef1 | 362 | { |
843894ad | 363 | fb->active_buf = pop_empty(fb); |
aa337ef1 SS |
364 | } |
365 | else | |
366 | { /* no empty or locked buffers, so use a readybuf */ | |
843894ad | 367 | fb->active_buf = pop_ready(fb); |
aa337ef1 SS |
368 | } |
369 | } | |
370 | } | |
371 | ||
372 | #ifdef DEBUG_QUES_B | |
843894ad | 373 | printques(fb); |
aa337ef1 SS |
374 | #endif |
375 | ||
17701d14 | 376 | fb->even_happened = 0; |
aa337ef1 | 377 | |
d241fd58 | 378 | wake_up_interruptible(&dt3155_read_wait_queue[minor]); |
aa337ef1 | 379 | |
aa337ef1 | 380 | local_irq_restore(flags); |
aa337ef1 SS |
381 | } |
382 | ||
383 | ||
384 | /* Set up the DMA address for the next frame/field */ | |
17701d14 | 385 | buffer_addr = fb->frame_info[fb->active_buf].addr; |
7d0f940e | 386 | if ((dts->state & DT3155_STATE_MODE) == DT3155_STATE_FLD) |
aa337ef1 | 387 | { |
55bb6ece | 388 | writel(buffer_addr, mmio + EVEN_DMA_START); |
aa337ef1 SS |
389 | } |
390 | else | |
391 | { | |
55bb6ece | 392 | writel(buffer_addr, mmio + EVEN_DMA_START); |
aa337ef1 | 393 | |
7d0f940e | 394 | writel(buffer_addr + dts->config.cols, mmio + ODD_DMA_START); |
aa337ef1 SS |
395 | } |
396 | ||
397 | /* Do error checking */ | |
398 | i2c_odd_csr.fld.DONE_ODD = 1; | |
d241fd58 | 399 | if (i2c_odd_csr.fld.ERROR_ODD) |
aa337ef1 SS |
400 | dt3155_errno = DT_ERR_OVERRUN; |
401 | ||
55bb6ece | 402 | WriteI2C(mmio, ODD_CSR, i2c_odd_csr.reg); |
aa337ef1 SS |
403 | |
404 | return; | |
405 | } | |
406 | /* If we get here, the Odd Field wasn't it either... */ | |
d241fd58 | 407 | printk("neither even nor odd. shared perhaps?\n"); |
aa337ef1 SS |
408 | } |
409 | ||
410 | /***************************************************** | |
411 | * init_isr(int minor) | |
412 | * turns on interupt generation for the card | |
413 | * designated by "minor". | |
414 | * It is called *only* from inside ioctl(). | |
415 | *****************************************************/ | |
416 | static void dt3155_init_isr(int minor) | |
417 | { | |
7d0f940e HS |
418 | struct dt3155_status *dts = &dt3155_status[minor]; |
419 | struct dt3155_fbuffer *fb = &dts->fbuffer; | |
55bb6ece | 420 | void __iomem *mmio = dt3155_lbase[minor]; |
aadbdeb6 HS |
421 | u32 dma_addr = fb->frame_info[fb->active_buf].addr; |
422 | const u32 stride = dts->config.cols; | |
423 | CSR1_R csr1_r; | |
424 | INT_CSR_R int_csr_r; | |
425 | I2C_CSR2 i2c_csr2; | |
aa337ef1 | 426 | |
7d0f940e | 427 | switch (dts->state & DT3155_STATE_MODE) |
aa337ef1 SS |
428 | { |
429 | case DT3155_STATE_FLD: | |
430 | { | |
aadbdeb6 HS |
431 | writel(dma_addr, mmio + EVEN_DMA_START); |
432 | writel(0, mmio + EVEN_DMA_STRIDE); | |
433 | writel(0, mmio + ODD_DMA_STRIDE); | |
aa337ef1 SS |
434 | break; |
435 | } | |
436 | ||
437 | case DT3155_STATE_FRAME: | |
438 | default: | |
439 | { | |
aadbdeb6 HS |
440 | writel(dma_addr, mmio + EVEN_DMA_START); |
441 | writel(dma_addr + stride, mmio + ODD_DMA_START); | |
442 | writel(stride, mmio + EVEN_DMA_STRIDE); | |
443 | writel(stride, mmio + ODD_DMA_STRIDE); | |
aa337ef1 SS |
444 | break; |
445 | } | |
446 | } | |
447 | ||
448 | /* 50/60 Hz should be set before this point but let's make sure it is */ | |
449 | /* right anyway */ | |
450 | ||
55bb6ece | 451 | ReadI2C(mmio, CSR2, &i2c_csr2.reg); |
aa337ef1 | 452 | i2c_csr2.fld.HZ50 = FORMAT50HZ; |
55bb6ece | 453 | WriteI2C(mmio, CSR2, i2c_csr2.reg); |
aa337ef1 SS |
454 | |
455 | /* enable busmaster chip, clear flags */ | |
456 | ||
457 | /* | |
458 | * TODO: | |
459 | * shouldn't we be concered with continuous values of | |
460 | * DT3155_SNAP & DT3155_ACQ here? (SS) | |
461 | */ | |
462 | ||
463 | csr1_r.reg = 0; | |
464 | csr1_r.fld.CAP_CONT_EVE = 1; /* use continuous capture bits to */ | |
465 | csr1_r.fld.CAP_CONT_ODD = 1; /* enable */ | |
466 | csr1_r.fld.FLD_DN_EVE = 1; /* writing a 1 clears flags */ | |
467 | csr1_r.fld.FLD_DN_ODD = 1; | |
468 | csr1_r.fld.SRST = 1; /* reset - must be 1 */ | |
469 | csr1_r.fld.FIFO_EN = 1; /* fifo control - must be 1 */ | |
470 | csr1_r.fld.FLD_CRPT_EVE = 1; /* writing a 1 clears flags */ | |
471 | csr1_r.fld.FLD_CRPT_ODD = 1; | |
472 | ||
55bb6ece | 473 | writel(csr1_r.reg, mmio + CSR1); |
aa337ef1 SS |
474 | |
475 | /* Enable interrupts at the end of each field */ | |
476 | ||
477 | int_csr_r.reg = 0; | |
478 | int_csr_r.fld.FLD_END_EVE_EN = 1; | |
479 | int_csr_r.fld.FLD_END_ODD_EN = 1; | |
480 | int_csr_r.fld.FLD_START_EN = 0; | |
481 | ||
55bb6ece | 482 | writel(int_csr_r.reg, mmio + INT_CSR); |
aa337ef1 SS |
483 | |
484 | /* start internal BUSY bits */ | |
485 | ||
55bb6ece | 486 | ReadI2C(mmio, CSR2, &i2c_csr2.reg); |
aa337ef1 SS |
487 | i2c_csr2.fld.BUSY_ODD = 1; |
488 | i2c_csr2.fld.BUSY_EVE = 1; | |
55bb6ece | 489 | WriteI2C(mmio, CSR2, i2c_csr2.reg); |
aa337ef1 SS |
490 | |
491 | /* Now its up to the interrupt routine!! */ | |
492 | ||
493 | return; | |
494 | } | |
495 | ||
496 | ||
497 | /***************************************************** | |
498 | * ioctl() | |
499 | * | |
500 | *****************************************************/ | |
dcff74ce GKH |
501 | static int dt3155_ioctl(struct inode *inode, |
502 | struct file *file, | |
503 | unsigned int cmd, | |
504 | unsigned long arg) | |
aa337ef1 SS |
505 | { |
506 | int minor = MINOR(inode->i_rdev); /* What device are we ioctl()'ing? */ | |
0f3ff30b | 507 | void __user *up = (void __user *)arg; |
7d0f940e HS |
508 | struct dt3155_status *dts = &dt3155_status[minor]; |
509 | struct dt3155_fbuffer *fb = &dts->fbuffer; | |
aa337ef1 | 510 | |
d241fd58 | 511 | if (minor >= MAXBOARDS || minor < 0) |
aa337ef1 SS |
512 | return -ENODEV; |
513 | ||
514 | /* make sure it is valid command */ | |
515 | if (_IOC_NR(cmd) > DT3155_IOC_MAXNR) | |
516 | { | |
517 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
518 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
519 | (unsigned int)DT3155_GET_CONFIG, |
520 | (unsigned int)DT3155_SET_CONFIG, | |
521 | (unsigned int)DT3155_START, | |
522 | (unsigned int)DT3155_STOP, | |
523 | (unsigned int)DT3155_FLUSH); | |
aa337ef1 SS |
524 | return -EINVAL; |
525 | } | |
526 | ||
527 | switch (cmd) | |
528 | { | |
529 | case DT3155_SET_CONFIG: | |
530 | { | |
7d0f940e | 531 | if (dts->state != DT3155_STATE_IDLE) |
aa337ef1 SS |
532 | return -EBUSY; |
533 | ||
534 | { | |
8b692e69 | 535 | struct dt3155_config tmp; |
0f3ff30b | 536 | if (copy_from_user(&tmp, up, sizeof(tmp))) |
aa337ef1 SS |
537 | return -EFAULT; |
538 | /* check for valid settings */ | |
539 | if (tmp.rows > DT3155_MAX_ROWS || | |
540 | tmp.cols > DT3155_MAX_COLS || | |
541 | (tmp.acq_mode != DT3155_MODE_FRAME && | |
542 | tmp.acq_mode != DT3155_MODE_FIELD) || | |
543 | (tmp.continuous != DT3155_SNAP && | |
544 | tmp.continuous != DT3155_ACQ)) | |
545 | { | |
546 | return -EINVAL; | |
547 | } | |
7d0f940e | 548 | dts->config = tmp; |
aa337ef1 SS |
549 | } |
550 | return 0; | |
551 | } | |
552 | case DT3155_GET_CONFIG: | |
553 | { | |
7d0f940e | 554 | if (copy_to_user(up, dts, sizeof(*dts))) |
aa337ef1 SS |
555 | return -EFAULT; |
556 | return 0; | |
557 | } | |
558 | case DT3155_FLUSH: /* Flushes the buffers -- ensures fresh data */ | |
559 | { | |
7d0f940e | 560 | if (dts->state != DT3155_STATE_IDLE) |
aa337ef1 | 561 | return -EBUSY; |
843894ad | 562 | return dt3155_flush(fb); |
aa337ef1 SS |
563 | } |
564 | case DT3155_STOP: | |
565 | { | |
7d0f940e | 566 | if (dts->state & DT3155_STATE_STOP || fb->stop_acquire) |
aa337ef1 SS |
567 | return -EBUSY; |
568 | ||
7d0f940e | 569 | if (dts->state == DT3155_STATE_IDLE) |
aa337ef1 SS |
570 | return 0; |
571 | ||
572 | quick_stop(minor); | |
7d0f940e | 573 | if (copy_to_user(up, dts, sizeof(*dts))) |
aa337ef1 SS |
574 | return -EFAULT; |
575 | return 0; | |
576 | } | |
577 | case DT3155_START: | |
578 | { | |
7d0f940e | 579 | if (dts->state != DT3155_STATE_IDLE) |
aa337ef1 SS |
580 | return -EBUSY; |
581 | ||
17701d14 HS |
582 | fb->stop_acquire = 0; |
583 | fb->frame_count = 0; | |
aa337ef1 SS |
584 | |
585 | /* Set the MODE in the status -- we default to FRAME */ | |
7d0f940e | 586 | if (dts->config.acq_mode == DT3155_MODE_FIELD) |
aa337ef1 | 587 | { |
7d0f940e | 588 | dts->state = DT3155_STATE_FLD; |
aa337ef1 SS |
589 | } |
590 | else | |
591 | { | |
7d0f940e | 592 | dts->state = DT3155_STATE_FRAME; |
aa337ef1 SS |
593 | } |
594 | ||
595 | dt3155_init_isr(minor); | |
7d0f940e | 596 | if (copy_to_user(up, dts, sizeof(*dts))) |
aa337ef1 SS |
597 | return -EFAULT; |
598 | return 0; | |
599 | } | |
600 | default: | |
601 | { | |
602 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
603 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
604 | (unsigned int)DT3155_GET_CONFIG, |
605 | (unsigned int)DT3155_SET_CONFIG, | |
aa337ef1 SS |
606 | DT3155_START, DT3155_STOP, DT3155_FLUSH); |
607 | return -ENOSYS; | |
608 | } | |
609 | } | |
610 | return -ENOSYS; | |
611 | } | |
612 | ||
613 | /***************************************************** | |
614 | * mmap() | |
615 | * | |
616 | * only allow the user to mmap the registers and buffer | |
617 | * It is quite possible that this is broken, since the | |
618 | * addition of of the capacity for two cards!!!!!!!! | |
619 | * It *looks* like it should work but since I'm not | |
620 | * sure how to use it, I'm not actually sure. (NJC? ditto by SS) | |
621 | *****************************************************/ | |
622 | static int dt3155_mmap (struct file * file, struct vm_area_struct * vma) | |
623 | { | |
624 | /* which device are we mmapping? */ | |
7d0f940e HS |
625 | int minor = MINOR(file->f_dentry->d_inode->i_rdev); |
626 | struct dt3155_status *dts = &dt3155_status[minor]; | |
aa337ef1 | 627 | unsigned long offset; |
aa337ef1 SS |
628 | offset = vma->vm_pgoff << PAGE_SHIFT; |
629 | ||
630 | if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) | |
631 | vma->vm_flags |= VM_IO; | |
632 | ||
633 | /* Don't try to swap out physical pages.. */ | |
634 | vma->vm_flags |= VM_RESERVED; | |
635 | ||
aa337ef1 | 636 | /* they are mapping the registers or the buffer */ |
7d0f940e | 637 | if ((offset == dts->reg_addr && |
aa337ef1 | 638 | vma->vm_end - vma->vm_start == PCI_PAGE_SIZE) || |
7d0f940e HS |
639 | (offset == dts->mem_addr && |
640 | vma->vm_end - vma->vm_start == dts->mem_size)) | |
aa337ef1 | 641 | { |
aa337ef1 SS |
642 | if (remap_pfn_range(vma, |
643 | vma->vm_start, | |
644 | offset >> PAGE_SHIFT, | |
645 | vma->vm_end - vma->vm_start, | |
2141ec62 | 646 | vma->vm_page_prot)) { |
aa337ef1 SS |
647 | printk("DT3155: remap_page_range() failed.\n"); |
648 | return -EAGAIN; | |
649 | } | |
650 | } | |
651 | else | |
652 | { | |
653 | printk("DT3155: dt3155_mmap() bad call.\n"); | |
654 | return -ENXIO; | |
655 | } | |
656 | ||
657 | return 0; | |
658 | } | |
659 | ||
660 | ||
661 | /***************************************************** | |
662 | * open() | |
663 | * | |
664 | * Our special open code. | |
665 | * MOD_INC_USE_COUNT make sure that the driver memory is not freed | |
666 | * while the device is in use. | |
667 | *****************************************************/ | |
d241fd58 | 668 | static int dt3155_open(struct inode* inode, struct file* filep) |
aa337ef1 SS |
669 | { |
670 | int minor = MINOR(inode->i_rdev); /* what device are we opening? */ | |
7d0f940e | 671 | struct dt3155_status *dts = &dt3155_status[minor]; |
843894ad | 672 | struct dt3155_fbuffer *fb = &dts->fbuffer; |
7d0f940e | 673 | |
d241fd58 | 674 | if (dt3155_dev_open[minor]) { |
aa337ef1 SS |
675 | printk ("DT3155: Already opened by another process.\n"); |
676 | return -EBUSY; | |
677 | } | |
678 | ||
7d0f940e | 679 | if (dts->device_installed==0) |
aa337ef1 SS |
680 | { |
681 | printk("DT3155 Open Error: No such device dt3155 minor number %d\n", | |
682 | minor); | |
683 | return -EIO; | |
684 | } | |
685 | ||
7d0f940e | 686 | if (dts->state != DT3155_STATE_IDLE) { |
dcff74ce | 687 | printk ("DT3155: Not in idle state (state = %x)\n", |
7d0f940e | 688 | dts->state); |
aa337ef1 SS |
689 | return -EBUSY; |
690 | } | |
691 | ||
692 | printk("DT3155: Device opened.\n"); | |
693 | ||
d241fd58 | 694 | dt3155_dev_open[minor] = 1 ; |
aa337ef1 | 695 | |
843894ad | 696 | dt3155_flush(fb); |
aa337ef1 SS |
697 | |
698 | /* Disable ALL interrupts */ | |
aadbdeb6 | 699 | writel(0, dt3155_lbase[minor] + INT_CSR); |
aa337ef1 | 700 | |
aa337ef1 | 701 | init_waitqueue_head(&(dt3155_read_wait_queue[minor])); |
aa337ef1 SS |
702 | |
703 | return 0; | |
704 | } | |
705 | ||
706 | ||
707 | /***************************************************** | |
708 | * close() | |
709 | * | |
710 | * Now decrement the use count. | |
711 | * | |
712 | *****************************************************/ | |
d241fd58 | 713 | static int dt3155_close(struct inode *inode, struct file *filep) |
aa337ef1 | 714 | { |
7d0f940e HS |
715 | int minor = MINOR(inode->i_rdev); /* which device are we closing */ |
716 | struct dt3155_status *dts = &dt3155_status[minor]; | |
aa337ef1 | 717 | |
d241fd58 | 718 | if (!dt3155_dev_open[minor]) |
aa337ef1 SS |
719 | { |
720 | printk("DT3155: attempt to CLOSE a not OPEN device\n"); | |
721 | } | |
722 | else | |
723 | { | |
d241fd58 | 724 | dt3155_dev_open[minor] = 0; |
aa337ef1 | 725 | |
7d0f940e | 726 | if (dts->state != DT3155_STATE_IDLE) |
aa337ef1 SS |
727 | { |
728 | quick_stop(minor); | |
729 | } | |
730 | } | |
731 | return 0; | |
732 | } | |
733 | ||
734 | /***************************************************** | |
735 | * read() | |
736 | * | |
737 | *****************************************************/ | |
f721ad7a GKH |
738 | static ssize_t dt3155_read(struct file *filep, char __user *buf, |
739 | size_t count, loff_t *ppos) | |
aa337ef1 SS |
740 | { |
741 | /* which device are we reading from? */ | |
742 | int minor = MINOR(filep->f_dentry->d_inode->i_rdev); | |
3a8954e8 | 743 | u32 offset; |
aa337ef1 | 744 | int frame_index; |
7d0f940e HS |
745 | struct dt3155_status *dts = &dt3155_status[minor]; |
746 | struct dt3155_fbuffer *fb = &dts->fbuffer; | |
7f76c52f | 747 | struct frame_info *frame_info; |
aa337ef1 SS |
748 | |
749 | /* TODO: this should check the error flag and */ | |
750 | /* return an error on hardware failures */ | |
5019d284 | 751 | if (count != sizeof(struct dt3155_read)) |
aa337ef1 SS |
752 | { |
753 | printk("DT3155 ERROR (NJC): count is not right\n"); | |
754 | return -EINVAL; | |
755 | } | |
756 | ||
757 | ||
758 | /* Hack here -- I'm going to allow reading even when idle. | |
759 | * this is so that the frames can be read after STOP has | |
760 | * been called. Leaving it here, commented out, as a reminder | |
761 | * for a short while to make sure there are no problems. | |
762 | * Note that if the driver is not opened in non_blocking mode, | |
763 | * and the device is idle, then it could sit here forever! */ | |
764 | ||
7d0f940e | 765 | /* if (dts->state == DT3155_STATE_IDLE)*/ |
aa337ef1 SS |
766 | /* return -EBUSY;*/ |
767 | ||
aa337ef1 SS |
768 | /* non-blocking reads should return if no data */ |
769 | if (filep->f_flags & O_NDELAY) | |
770 | { | |
843894ad HS |
771 | if ((frame_index = dt3155_get_ready_buffer(fb)) < 0) { |
772 | /* printk("dt3155: no buffers available (?)\n"); */ | |
773 | /* printques(fb); */ | |
aa337ef1 SS |
774 | return -EAGAIN; |
775 | } | |
776 | } | |
777 | else | |
778 | { | |
779 | /* | |
780 | * sleep till data arrives , or we get interrupted. | |
781 | * Note that wait_event_interruptible() does not actually | |
782 | * sleep/wait if it's condition evaluates to true upon entry. | |
783 | */ | |
843894ad HS |
784 | frame_index = dt3155_get_ready_buffer(fb); |
785 | wait_event_interruptible(dt3155_read_wait_queue[minor], frame_index >= 0); | |
aa337ef1 SS |
786 | |
787 | if (frame_index < 0) | |
788 | { | |
789 | printk ("DT3155: read: interrupted\n"); | |
790 | quick_stop (minor); | |
843894ad | 791 | printques(fb); |
aa337ef1 SS |
792 | return -EINTR; |
793 | } | |
794 | } | |
795 | ||
17701d14 | 796 | frame_info = &fb->frame_info[frame_index]; |
aa337ef1 SS |
797 | |
798 | /* make this an offset */ | |
7d0f940e | 799 | offset = frame_info->addr - dts->mem_addr; |
aa337ef1 | 800 | |
0f3ff30b | 801 | put_user(offset, (unsigned int __user *)buf); |
3a8954e8 | 802 | buf += sizeof(u32); |
17701d14 | 803 | put_user(fb->frame_count, (unsigned int __user *)buf); |
3a8954e8 | 804 | buf += sizeof(u32); |
7d0f940e | 805 | put_user(dts->state, (unsigned int __user *)buf); |
3a8954e8 | 806 | buf += sizeof(u32); |
7f76c52f | 807 | if (copy_to_user(buf, frame_info, sizeof(*frame_info))) |
aa337ef1 SS |
808 | return -EFAULT; |
809 | ||
5019d284 | 810 | return sizeof(struct dt3155_read); |
aa337ef1 SS |
811 | } |
812 | ||
813 | static unsigned int dt3155_poll (struct file * filp, poll_table *wait) | |
814 | { | |
815 | int minor = MINOR(filp->f_dentry->d_inode->i_rdev); | |
843894ad HS |
816 | struct dt3155_status *dts = &dt3155_status[minor]; |
817 | struct dt3155_fbuffer *fb = &dts->fbuffer; | |
aa337ef1 | 818 | |
843894ad | 819 | if (!is_ready_buf_empty(fb)) |
aa337ef1 SS |
820 | return POLLIN | POLLRDNORM; |
821 | ||
822 | poll_wait (filp, &dt3155_read_wait_queue[minor], wait); | |
823 | ||
824 | return 0; | |
825 | } | |
826 | ||
b1f2ac07 AB |
827 | static long |
828 | dt3155_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
829 | { | |
830 | int ret; | |
831 | ||
8e2394a9 | 832 | mutex_lock(&dt3155_mutex); |
b1f2ac07 | 833 | ret = dt3155_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); |
8e2394a9 | 834 | mutex_unlock(&dt3155_mutex); |
b1f2ac07 AB |
835 | |
836 | return ret; | |
837 | } | |
aa337ef1 SS |
838 | |
839 | /***************************************************** | |
840 | * file operations supported by DT3155 driver | |
841 | * needed by init_module | |
842 | * register_chrdev | |
843 | *****************************************************/ | |
844 | static struct file_operations dt3155_fops = { | |
b1f2ac07 AB |
845 | .read = dt3155_read, |
846 | .unlocked_ioctl = dt3155_unlocked_ioctl, | |
847 | .mmap = dt3155_mmap, | |
848 | .poll = dt3155_poll, | |
849 | .open = dt3155_open, | |
850 | .release = dt3155_close | |
aa337ef1 SS |
851 | }; |
852 | ||
853 | ||
854 | /***************************************************** | |
855 | * find_PCI(); | |
856 | * | |
857 | * PCI has been totally reworked in 2.1.. | |
858 | *****************************************************/ | |
859 | static int find_PCI (void) | |
860 | { | |
861 | struct pci_dev *pci_dev = NULL; | |
7d0f940e | 862 | struct dt3155_status *dts; |
aa337ef1 SS |
863 | int error, pci_index = 0; |
864 | unsigned short rev_device; | |
865 | unsigned long base; | |
866 | unsigned char irq; | |
867 | ||
6910dadf | 868 | while ((pci_dev = pci_get_device |
aa337ef1 SS |
869 | (DT3155_VENDORID, DT3155_DEVICEID, pci_dev)) != NULL) |
870 | { | |
7d0f940e | 871 | dts = &dt3155_status[pci_index++]; |
aa337ef1 SS |
872 | |
873 | /* Is it really there? */ | |
874 | if ((error = | |
875 | pci_read_config_word(pci_dev, PCI_CLASS_DEVICE, &rev_device))) | |
876 | continue; | |
877 | ||
878 | /* Found a board */ | |
879 | DT_3155_DEBUG_MSG("DT3155: Device number %d \n", pci_index); | |
880 | ||
881 | /* Make sure the driver was compiled with enough buffers to handle | |
882 | this many boards */ | |
883 | if (pci_index > MAXBOARDS) { | |
884 | printk("DT3155: ERROR - found %d devices, but driver only configured " | |
885 | "for %d devices\n" | |
886 | "DT3155: Please change MAXBOARDS in dt3155.h\n", | |
887 | pci_index, MAXBOARDS); | |
6910dadf | 888 | goto err; |
aa337ef1 SS |
889 | } |
890 | ||
891 | /* Now, just go out and make sure that this/these device(s) is/are | |
892 | actually mapped into the kernel address space */ | |
d241fd58 | 893 | if ((error = pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, |
dcff74ce | 894 | (u32 *) &base))) |
aa337ef1 SS |
895 | { |
896 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 897 | goto err; |
aa337ef1 SS |
898 | } |
899 | ||
900 | DT_3155_DEBUG_MSG("DT3155: Base address 0 for device is %lx \n", base); | |
7d0f940e | 901 | dts->reg_addr = base; |
aa337ef1 SS |
902 | |
903 | /* Remap the base address to a logical address through which we | |
904 | * can access it. */ | |
55bb6ece | 905 | dt3155_lbase[pci_index - 1] = ioremap(base, PCI_PAGE_SIZE); |
7d0f940e | 906 | dts->reg_addr = base; |
f721ad7a GKH |
907 | DT_3155_DEBUG_MSG("DT3155: New logical address is %p \n", |
908 | dt3155_lbase[pci_index-1]); | |
d241fd58 | 909 | if (!dt3155_lbase[pci_index-1]) |
aa337ef1 SS |
910 | { |
911 | printk("DT3155: Unable to remap control registers\n"); | |
6910dadf | 912 | goto err; |
aa337ef1 SS |
913 | } |
914 | ||
d241fd58 | 915 | if ((error = pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &irq))) |
aa337ef1 SS |
916 | { |
917 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 918 | goto err; |
aa337ef1 SS |
919 | } |
920 | ||
921 | DT_3155_DEBUG_MSG("DT3155: IRQ is %d \n",irq); | |
7d0f940e | 922 | dts->irq = irq; |
aa337ef1 | 923 | /* Set flag: kth device found! */ |
7d0f940e | 924 | dts->device_installed = 1; |
f721ad7a | 925 | printk("DT3155: Installing device %d w/irq %d and address %p\n", |
aa337ef1 | 926 | pci_index, |
7d0f940e | 927 | dts->irq, |
f721ad7a | 928 | dt3155_lbase[pci_index-1]); |
aa337ef1 SS |
929 | |
930 | } | |
931 | ndevices = pci_index; | |
932 | ||
a46f9087 | 933 | return 0; |
6910dadf SH |
934 | |
935 | err: | |
936 | pci_dev_put(pci_dev); | |
a46f9087 | 937 | return -EIO; |
aa337ef1 SS |
938 | } |
939 | ||
3a8954e8 | 940 | u32 allocatorAddr = 0; |
aa337ef1 SS |
941 | |
942 | /***************************************************** | |
943 | * init_module() | |
944 | *****************************************************/ | |
945 | int init_module(void) | |
946 | { | |
7d0f940e | 947 | struct dt3155_status *dts; |
aa337ef1 SS |
948 | int index; |
949 | int rcode = 0; | |
d241fd58 | 950 | char *devname[MAXBOARDS]; |
aa337ef1 | 951 | |
d241fd58 | 952 | devname[0] = "dt3155a"; |
aa337ef1 | 953 | #if MAXBOARDS == 2 |
d241fd58 | 954 | devname[1] = "dt3155b"; |
aa337ef1 SS |
955 | #endif |
956 | ||
957 | printk("DT3155: Loading module...\n"); | |
958 | ||
959 | /* Register the device driver */ | |
d241fd58 JB |
960 | rcode = register_chrdev(dt3155_major, "dt3155", &dt3155_fops); |
961 | if(rcode < 0) | |
aa337ef1 | 962 | { |
d241fd58 | 963 | printk(KERN_INFO "DT3155: register_chrdev failed \n"); |
aa337ef1 SS |
964 | return rcode; |
965 | } | |
966 | ||
d241fd58 | 967 | if(dt3155_major == 0) |
aa337ef1 SS |
968 | dt3155_major = rcode; /* dynamic */ |
969 | ||
970 | ||
971 | /* init the status variables. */ | |
972 | /* DMA memory is taken care of in setup_buffers() */ | |
d241fd58 | 973 | for (index = 0; index < MAXBOARDS; index++) |
aa337ef1 | 974 | { |
7d0f940e HS |
975 | dts = &dt3155_status[index]; |
976 | ||
977 | dts->config.acq_mode = DT3155_MODE_FRAME; | |
978 | dts->config.continuous = DT3155_ACQ; | |
979 | dts->config.cols = DT3155_MAX_COLS; | |
980 | dts->config.rows = DT3155_MAX_ROWS; | |
981 | dts->state = DT3155_STATE_IDLE; | |
aa337ef1 SS |
982 | |
983 | /* find_PCI() will check if devices are installed; */ | |
984 | /* first assume they're not: */ | |
7d0f940e HS |
985 | dts->mem_addr = 0; |
986 | dts->mem_size = 0; | |
987 | dts->state = DT3155_STATE_IDLE; | |
988 | dts->device_installed = 0; | |
aa337ef1 SS |
989 | } |
990 | ||
991 | /* Now let's find the hardware. find_PCI() will set ndevices to the | |
992 | * number of cards found in this machine. */ | |
aa337ef1 | 993 | { |
a46f9087 | 994 | if ((rcode = find_PCI()) != 0) |
aa337ef1 SS |
995 | { |
996 | printk("DT3155 error: find_PCI() failed to find dt3155 board(s)\n"); | |
d241fd58 | 997 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
998 | return rcode; |
999 | } | |
1000 | } | |
1001 | ||
1002 | /* Ok, time to setup the frame buffers */ | |
d241fd58 | 1003 | if((rcode = dt3155_setup_buffers(&allocatorAddr)) < 0) |
aa337ef1 SS |
1004 | { |
1005 | printk("DT3155: Error: setting up buffer not large enough."); | |
d241fd58 | 1006 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
1007 | return rcode; |
1008 | } | |
1009 | ||
1010 | /* If we are this far, then there is enough RAM */ | |
1011 | /* for the buffers: Print the configuration. */ | |
d241fd58 | 1012 | for( index = 0; index < ndevices; index++) |
aa337ef1 | 1013 | { |
7d0f940e HS |
1014 | dts = &dt3155_status[index]; |
1015 | ||
aa337ef1 SS |
1016 | printk("DT3155: Device = %d; acq_mode = %d; " |
1017 | "continuous = %d; cols = %d; rows = %d;\n", | |
1018 | index , | |
7d0f940e HS |
1019 | dts->config.acq_mode, |
1020 | dts->config.continuous, | |
1021 | dts->config.cols, | |
1022 | dts->config.rows); | |
aa337ef1 | 1023 | printk("DT3155: m_addr = 0x%x; m_size = %ld; " |
dcff74ce | 1024 | "state = %d; device_installed = %d\n", |
7d0f940e HS |
1025 | dts->mem_addr, |
1026 | (long int)dts->mem_size, | |
1027 | dts->state, | |
1028 | dts->device_installed); | |
aa337ef1 SS |
1029 | } |
1030 | ||
1031 | /* Disable ALL interrupts */ | |
d241fd58 | 1032 | for( index = 0; index < ndevices; index++) |
aa337ef1 | 1033 | { |
7d0f940e HS |
1034 | dts = &dt3155_status[index]; |
1035 | ||
aadbdeb6 | 1036 | writel(0, dt3155_lbase[index] + INT_CSR); |
7d0f940e | 1037 | if(dts->device_installed) |
aa337ef1 SS |
1038 | { |
1039 | /* | |
1040 | * This driver *looks* like it can handle sharing interrupts, | |
1041 | * but I can't actually test myself. I've had reports that it | |
1042 | * DOES work so I'll enable it for now. This comment will remain | |
1043 | * as a reminder in case any problems arise. (SS) | |
1044 | */ | |
1045 | /* in older kernels flags are: SA_SHIRQ | SA_INTERRUPT */ | |
7d0f940e | 1046 | rcode = request_irq(dts->irq, (void *)dt3155_isr, |
d241fd58 | 1047 | IRQF_SHARED | IRQF_DISABLED, devname[index], |
7d0f940e | 1048 | (void *)dts); |
d241fd58 | 1049 | if(rcode < 0) |
aa337ef1 SS |
1050 | { |
1051 | printk("DT3155: minor %d request_irq failed for IRQ %d\n", | |
7d0f940e | 1052 | index, dts->irq); |
d241fd58 | 1053 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
1054 | return rcode; |
1055 | } | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | printk("DT3155: finished loading\n"); | |
1060 | ||
1061 | return 0; | |
1062 | } | |
1063 | ||
1064 | /***************************************************** | |
1065 | * cleanup_module(void) | |
1066 | * | |
1067 | *****************************************************/ | |
1068 | void cleanup_module(void) | |
1069 | { | |
7d0f940e | 1070 | struct dt3155_status *dts; |
aa337ef1 SS |
1071 | int index; |
1072 | ||
1073 | printk("DT3155: cleanup_module called\n"); | |
1074 | ||
1075 | /* removed DMA allocated with the allocator */ | |
1076 | #ifdef STANDALONE_ALLOCATOR | |
1077 | if (allocatorAddr != 0) | |
1078 | allocator_free_dma(allocatorAddr); | |
1079 | #else | |
1080 | allocator_cleanup(); | |
1081 | #endif | |
1082 | ||
d241fd58 | 1083 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 | 1084 | |
d241fd58 | 1085 | for(index = 0; index < ndevices; index++) |
aa337ef1 | 1086 | { |
7d0f940e HS |
1087 | dts = &dt3155_status[index]; |
1088 | if(dts->device_installed == 1) | |
aa337ef1 | 1089 | { |
d241fd58 | 1090 | printk("DT3155: Freeing irq %d for device %d\n", |
7d0f940e HS |
1091 | dts->irq, index); |
1092 | free_irq(dts->irq, (void *)dts); | |
aa337ef1 SS |
1093 | } |
1094 | } | |
aa337ef1 SS |
1095 | } |
1096 |