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