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