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 | File: dt3155_isr.c | |
24 | Purpose: Buffer management routines, and other routines for the ISR | |
25 | (the actual isr is in dt3155_drv.c) | |
26 | ||
27 | -- Changes -- | |
28 | ||
29 | Date Programmer Description of changes made | |
30 | ------------------------------------------------------------------- | |
31 | 03-Jul-2000 JML n/a | |
32 | 02-Apr-2002 SS Mods to make work with separate allocator | |
33 | module; Merged John Roll's mods to make work with | |
34 | multiple boards. | |
35 | 10-Jul-2002 GCS Complete rewrite of setup_buffers to disallow | |
36 | buffers which span a 4MB boundary. | |
37 | 24-Jul-2002 SS GPL licence. | |
38 | 30-Jul-2002 NJC Added support for buffer loop. | |
39 | 31-Jul-2002 NJC Complete rewrite of buffer management | |
40 | 02-Aug-2002 NJC Including slab.h instead of malloc.h (no warning). | |
41 | Also, allocator_init() now returns allocator_max | |
42 | so cleaned up allocate_buffers() accordingly. | |
43 | 08-Aug-2005 SS port to 2.6 kernel. | |
44 | ||
45 | */ | |
46 | ||
47 | #include <asm/system.h> | |
48 | #include <linux/slab.h> | |
49 | #include <linux/sched.h> | |
50 | #include <linux/types.h> | |
51 | ||
52 | #include "dt3155.h" | |
53 | #include "dt3155_drv.h" | |
54 | #include "dt3155_io.h" | |
55 | #include "dt3155_isr.h" | |
56 | #include "allocator.h" | |
57 | ||
58 | #define FOUR_MB (0x0400000) /* Can't DMA accross a 4MB boundary!*/ | |
59 | #define UPPER_10_BITS (0x3FF<<22) /* Can't DMA accross a 4MB boundary!*/ | |
60 | ||
61 | ||
62 | /* Pointer into global structure for handling buffers */ | |
63 | struct dt3155_fbuffer_s *dt3155_fbuffer[MAXBOARDS] = {NULL | |
64 | #if MAXBOARDS == 2 | |
65 | , NULL | |
66 | #endif | |
67 | }; | |
68 | ||
69 | /****************************************************************************** | |
70 | * Simple array based que struct | |
71 | * | |
72 | * Some handy functions using the buffering structure. | |
73 | *****************************************************************************/ | |
74 | ||
75 | ||
76 | /*************************** | |
77 | * are_empty_buffers | |
78 | * m is minor # of device | |
79 | ***************************/ | |
80 | inline bool are_empty_buffers( int m ) | |
81 | { | |
82 | return ( dt3155_fbuffer[ m ]->empty_len ); | |
83 | } | |
84 | ||
85 | /************************** | |
86 | * push_empty | |
87 | * m is minor # of device | |
88 | * | |
89 | * This is slightly confusing. The number empty_len is the literal # | |
90 | * of empty buffers. After calling, empty_len-1 is the index into the | |
91 | * empty buffer stack. So, if empty_len == 1, there is one empty buffer, | |
92 | * given by dt3155_fbuffer[m]->empty_buffers[0]. | |
93 | * empty_buffers should never fill up, though this is not checked. | |
94 | **************************/ | |
95 | inline void push_empty( int index, int m ) | |
96 | { | |
97 | dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ] = index; | |
98 | dt3155_fbuffer[m]->empty_len++; | |
99 | } | |
100 | ||
101 | /************************** | |
102 | * pop_empty( m ) | |
103 | * m is minor # of device | |
104 | **************************/ | |
105 | inline int pop_empty( int m ) | |
106 | { | |
107 | dt3155_fbuffer[m]->empty_len--; | |
108 | return dt3155_fbuffer[m]->empty_buffers[ dt3155_fbuffer[m]->empty_len ]; | |
109 | } | |
110 | ||
111 | /************************* | |
112 | * is_ready_buf_empty( m ) | |
113 | * m is minor # of device | |
114 | *************************/ | |
115 | inline bool is_ready_buf_empty( int m ) | |
116 | { | |
117 | return ((dt3155_fbuffer[ m ]->ready_len) == 0); | |
118 | } | |
119 | ||
120 | /************************* | |
121 | * is_ready_buf_full( m ) | |
122 | * m is minor # of device | |
123 | * this should *never* be true if there are any active, locked or empty | |
124 | * buffers, since it corresponds to nbuffers ready buffers!! | |
125 | * 7/31/02: total rewrite. --NJC | |
126 | *************************/ | |
127 | inline bool is_ready_buf_full( int m ) | |
128 | { | |
129 | return ( dt3155_fbuffer[ m ]->ready_len == dt3155_fbuffer[ m ]->nbuffers ); | |
130 | } | |
131 | ||
132 | /***************************************************** | |
133 | * push_ready( m, buffer ) | |
134 | * m is minor # of device | |
135 | * | |
136 | *****************************************************/ | |
137 | inline void push_ready( int m, int index ) | |
138 | { | |
139 | int head = dt3155_fbuffer[m]->ready_head; | |
140 | ||
141 | dt3155_fbuffer[ m ]->ready_que[ head ] = index; | |
142 | dt3155_fbuffer[ m ]->ready_head = ( (head + 1) % | |
143 | (dt3155_fbuffer[ m ]->nbuffers) ); | |
144 | dt3155_fbuffer[ m ]->ready_len++; | |
145 | ||
146 | } | |
147 | ||
148 | /***************************************************** | |
149 | * get_tail() | |
150 | * m is minor # of device | |
151 | * | |
152 | * Simply comptutes the tail given the head and the length. | |
153 | *****************************************************/ | |
154 | static inline int get_tail( int m ) | |
155 | { | |
156 | return ((dt3155_fbuffer[ m ]->ready_head - | |
157 | dt3155_fbuffer[ m ]->ready_len + | |
158 | dt3155_fbuffer[ m ]->nbuffers)% | |
159 | (dt3155_fbuffer[ m ]->nbuffers)); | |
160 | } | |
161 | ||
162 | ||
163 | ||
164 | /***************************************************** | |
165 | * pop_ready() | |
166 | * m is minor # of device | |
167 | * | |
168 | * This assumes that there is a ready buffer ready... should | |
169 | * be checked (e.g. with is_ready_buf_empty() prior to call. | |
170 | *****************************************************/ | |
171 | inline int pop_ready( int m ) | |
172 | { | |
173 | int tail; | |
174 | tail = get_tail(m); | |
175 | dt3155_fbuffer[ m ]->ready_len--; | |
176 | return dt3155_fbuffer[ m ]->ready_que[ tail ]; | |
177 | } | |
178 | ||
179 | ||
180 | /***************************************************** | |
181 | * printques | |
182 | * m is minor # of device | |
183 | *****************************************************/ | |
184 | inline void printques( int m ) | |
185 | { | |
186 | int head = dt3155_fbuffer[ m ]->ready_head; | |
187 | int tail; | |
188 | int num = dt3155_fbuffer[ m ]->nbuffers; | |
189 | int frame_index; | |
190 | int index; | |
191 | ||
192 | tail = get_tail(m); | |
193 | ||
194 | printk("\n R:"); | |
195 | for ( index = tail; index != head; index++, index = index % (num) ) | |
196 | { | |
197 | frame_index = dt3155_fbuffer[ m ]->ready_que[ index ]; | |
198 | printk(" %d ", frame_index ); | |
199 | } | |
200 | ||
201 | printk("\n E:"); | |
202 | for ( index = 0; index < dt3155_fbuffer[ m ]->empty_len; index++ ) | |
203 | { | |
204 | frame_index = dt3155_fbuffer[ m ]->empty_buffers[ index ]; | |
205 | printk(" %d ", frame_index ); | |
206 | } | |
207 | ||
208 | frame_index = dt3155_fbuffer[ m ]->active_buf; | |
209 | printk("\n A: %d", frame_index); | |
210 | ||
211 | frame_index = dt3155_fbuffer[ m ]->locked_buf; | |
212 | printk("\n L: %d \n", frame_index ); | |
213 | ||
214 | } | |
215 | ||
216 | /***************************************************** | |
217 | * adjust_4MB | |
218 | * | |
219 | * If a buffer intersects the 4MB boundary, push | |
220 | * the start address up to the beginning of the | |
221 | * next 4MB chunk (assuming bufsize < 4MB). | |
222 | *****************************************************/ | |
223 | u_long adjust_4MB (u_long buf_addr, u_long bufsize) { | |
224 | if (((buf_addr+bufsize) & UPPER_10_BITS) != (buf_addr & UPPER_10_BITS)) | |
225 | return (buf_addr+bufsize) & UPPER_10_BITS; | |
226 | else | |
227 | return buf_addr; | |
228 | } | |
229 | ||
230 | ||
231 | /***************************************************** | |
232 | * allocate_buffers | |
233 | * | |
234 | * Try to allocate enough memory for all requested | |
235 | * buffers. If there is not enough free space | |
236 | * try for less memory. | |
237 | *****************************************************/ | |
238 | void allocate_buffers (u_long *buf_addr, u_long* total_size_kbs, | |
239 | u_long bufsize) | |
240 | { | |
241 | /* Compute the minimum amount of memory guaranteed to hold all | |
242 | MAXBUFFERS such that no buffer crosses the 4MB boundary. | |
243 | Store this value in the variable "full_size" */ | |
244 | ||
245 | u_long allocator_max; | |
246 | u_long bufs_per_chunk = (FOUR_MB / bufsize); | |
247 | u_long filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk; | |
248 | u_long leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk; | |
249 | ||
250 | u_long full_size = bufsize /* possibly unusable part of 1st chunk */ | |
251 | + filled_chunks * FOUR_MB /* max # of completely filled 4mb chunks */ | |
252 | + leftover_bufs * bufsize; /* these buffs will be in a partly filled | |
253 | chunk at beginning or end */ | |
254 | ||
255 | u_long full_size_kbs = 1 + (full_size-1) / 1024; | |
256 | u_long min_size_kbs = 2*ndevices*bufsize / 1024; | |
257 | u_long size_kbs; | |
258 | ||
259 | /* Now, try to allocate full_size. If this fails, keep trying for | |
260 | less & less memory until it succeeds. */ | |
261 | #ifndef STANDALONE_ALLOCATOR | |
262 | /* initialize the allocator */ | |
263 | allocator_init(&allocator_max); | |
264 | #endif | |
265 | size_kbs = full_size_kbs; | |
266 | *buf_addr = 0; | |
267 | printk ("DT3155: We would like to get: %d KB\n", (u_int)(full_size_kbs)); | |
268 | printk ("DT3155: ...but need at least: %d KB\n", (u_int)(min_size_kbs)); | |
269 | printk ("DT3155: ...the allocator has: %d KB\n", (u_int)(allocator_max)); | |
270 | size_kbs = (full_size_kbs <= allocator_max ? full_size_kbs : allocator_max); | |
271 | if (size_kbs > min_size_kbs) { | |
272 | if ((*buf_addr = allocator_allocate_dma (size_kbs, GFP_KERNEL)) != 0) { | |
273 | printk ("DT3155: Managed to allocate: %d KB\n", (u_int)size_kbs); | |
274 | *total_size_kbs = size_kbs; | |
275 | return; | |
276 | } | |
277 | } | |
278 | /* If we got here, the allocation failed */ | |
279 | printk ("DT3155: Allocator failed!\n"); | |
280 | *buf_addr = 0; | |
281 | *total_size_kbs = 0; | |
282 | return; | |
283 | ||
284 | } | |
285 | ||
286 | ||
287 | /***************************************************** | |
288 | * dt3155_setup_buffers | |
289 | * | |
290 | * setup_buffers just puts the buffering system into | |
291 | * a consistent state before the start of interrupts | |
292 | * | |
293 | * JML : it looks like all the buffers need to be | |
294 | * continuous. So I'm going to try and allocate one | |
295 | * continuous buffer. | |
296 | * | |
297 | * GCS : Fix DMA problems when buffer spans | |
298 | * 4MB boundary. Also, add error checking. This | |
299 | * function will return -ENOMEM when not enough memory. | |
300 | *****************************************************/ | |
301 | u_long dt3155_setup_buffers(u_long *allocatorAddr) | |
302 | ||
303 | { | |
304 | u_long index; | |
305 | u_long rambuff_addr; /* start of allocation */ | |
306 | u_long rambuff_size; /* total size allocated to driver */ | |
307 | u_long rambuff_acm; /* accumlator, keep track of how much | |
308 | is left after being split up*/ | |
309 | u_long rambuff_end; /* end of rambuff */ | |
310 | u_long numbufs; /* number of useful buffers allocated (per device) */ | |
311 | u_long bufsize = DT3155_MAX_ROWS * DT3155_MAX_COLS; | |
312 | int m; /* minor # of device, looped for all devs */ | |
313 | ||
314 | /* zero the fbuffer status and address structure */ | |
315 | for ( m = 0; m < ndevices; m++) | |
316 | { | |
317 | dt3155_fbuffer[ m ] = &(dt3155_status[ m ].fbuffer); | |
318 | ||
319 | /* Make sure the buffering variables are consistent */ | |
320 | { | |
321 | u_char *ptr = (u_char *) dt3155_fbuffer[ m ]; | |
322 | for( index = 0; index < sizeof(struct dt3155_fbuffer_s); index++) | |
323 | *(ptr++)=0; | |
324 | } | |
325 | } | |
326 | ||
327 | /* allocate a large contiguous chunk of RAM */ | |
328 | allocate_buffers (&rambuff_addr, &rambuff_size, bufsize); | |
329 | printk( "DT3155: mem info\n" ); | |
330 | printk( " - rambuf_addr = 0x%x \n", (u_int)rambuff_addr ); | |
331 | printk( " - length (kb) = %u \n", (u_int)rambuff_size ); | |
332 | if( rambuff_addr == 0 ) | |
333 | { | |
334 | printk( KERN_INFO | |
335 | "DT3155: Error setup_buffers() allocator dma failed \n" ); | |
336 | return -ENOMEM; | |
337 | } | |
338 | *allocatorAddr = rambuff_addr; | |
339 | rambuff_end = rambuff_addr + 1024 * rambuff_size; | |
340 | ||
341 | /* after allocation, we need to count how many useful buffers there | |
342 | are so we can give an equal number to each device */ | |
343 | rambuff_acm = rambuff_addr; | |
344 | for ( index = 0; index < MAXBUFFERS; index++) { | |
345 | rambuff_acm = adjust_4MB (rambuff_acm, bufsize);/*avoid spanning 4MB bdry*/ | |
346 | if (rambuff_acm + bufsize > rambuff_end) | |
347 | break; | |
348 | rambuff_acm += bufsize; | |
349 | } | |
350 | /* Following line is OK, will waste buffers if index | |
351 | * not evenly divisible by ndevices -NJC*/ | |
352 | numbufs = index / ndevices; | |
353 | printk (" - numbufs = %u\n", (u_int) numbufs); | |
354 | if (numbufs < 2) { | |
355 | printk( KERN_INFO | |
356 | "DT3155: Error setup_buffers() couldn't allocate 2 bufs/board\n" ); | |
357 | return -ENOMEM; | |
358 | } | |
359 | ||
360 | /* now that we have board memory we spit it up */ | |
361 | /* between the boards and the buffers */ | |
362 | rambuff_acm = rambuff_addr; | |
363 | for ( m = 0; m < ndevices; m ++) | |
364 | { | |
365 | rambuff_acm = adjust_4MB (rambuff_acm, bufsize); | |
366 | ||
367 | /* Save the start of this boards buffer space (for mmap). */ | |
368 | dt3155_status[ m ].mem_addr = rambuff_acm; | |
369 | ||
370 | for (index = 0; index < numbufs; index++) | |
371 | { | |
372 | rambuff_acm = adjust_4MB (rambuff_acm, bufsize); | |
373 | if (rambuff_acm + bufsize > rambuff_end) { | |
374 | /* Should never happen */ | |
375 | printk ("DT3155 PROGRAM ERROR (GCS)\n" | |
376 | "Error distributing allocated buffers\n"); | |
377 | return -ENOMEM; | |
378 | } | |
379 | ||
380 | dt3155_fbuffer[ m ]->frame_info[ index ].addr = rambuff_acm; | |
381 | push_empty( index, m ); | |
382 | /* printk(" - Buffer : %lx\n", | |
383 | * dt3155_fbuffer[ m ]->frame_info[ index ].addr ); | |
384 | */ | |
385 | dt3155_fbuffer[ m ]->nbuffers += 1; | |
386 | rambuff_acm += bufsize; | |
387 | } | |
388 | ||
389 | /* Make sure there is an active buffer there. */ | |
390 | dt3155_fbuffer[ m ]->active_buf = pop_empty( m ); | |
391 | dt3155_fbuffer[ m ]->even_happened = 0; | |
392 | dt3155_fbuffer[ m ]->even_stopped = 0; | |
393 | ||
394 | /* make sure there is no locked_buf JML 2/28/00 */ | |
395 | dt3155_fbuffer[ m ]->locked_buf = -1; | |
396 | ||
397 | dt3155_status[ m ].mem_size = | |
398 | rambuff_acm - dt3155_status[ m ].mem_addr; | |
399 | ||
400 | /* setup the ready queue */ | |
401 | dt3155_fbuffer[ m ]->ready_head = 0; | |
402 | dt3155_fbuffer[ m ]->ready_len = 0; | |
403 | printk("Available buffers for device %d: %d\n", | |
404 | m, dt3155_fbuffer[ m ]->nbuffers); | |
405 | } | |
406 | ||
407 | return 1; | |
408 | } | |
409 | ||
410 | /***************************************************** | |
411 | * internal_release_locked_buffer | |
412 | * | |
413 | * The internal function for releasing a locked buffer. | |
414 | * It assumes interrupts are turned off. | |
415 | * | |
416 | * m is minor number of device | |
417 | *****************************************************/ | |
418 | static inline void internal_release_locked_buffer( int m ) | |
419 | { | |
420 | /* Pointer into global structure for handling buffers */ | |
421 | if ( dt3155_fbuffer[ m ]->locked_buf >= 0 ) | |
422 | { | |
423 | push_empty( dt3155_fbuffer[ m ]->locked_buf, m ); | |
424 | dt3155_fbuffer[ m ]->locked_buf = -1; | |
425 | } | |
426 | } | |
427 | ||
428 | ||
429 | /***************************************************** | |
430 | * dt3155_release_locked_buffer() | |
431 | * m is minor # of device | |
432 | * | |
433 | * The user function of the above. | |
434 | * | |
435 | *****************************************************/ | |
436 | inline void dt3155_release_locked_buffer( int m ) | |
437 | { | |
438 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
439 | unsigned long int flags; | |
440 | local_save_flags(flags); | |
441 | local_irq_disable(); | |
442 | internal_release_locked_buffer(m); | |
443 | local_irq_restore(flags); | |
444 | #else | |
445 | int flags; | |
446 | ||
447 | save_flags( flags ); | |
448 | cli(); | |
449 | internal_release_locked_buffer( m ); | |
450 | restore_flags( flags ); | |
451 | #endif | |
452 | } | |
453 | ||
454 | ||
455 | /***************************************************** | |
456 | * dt3155_flush() | |
457 | * m is minor # of device | |
458 | * | |
459 | *****************************************************/ | |
460 | inline int dt3155_flush( int m ) | |
461 | { | |
462 | int index; | |
463 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
464 | unsigned long int flags; | |
465 | local_save_flags(flags); | |
466 | local_irq_disable(); | |
467 | #else | |
468 | int flags; | |
469 | save_flags( flags ); | |
470 | cli(); | |
471 | #endif | |
472 | ||
473 | internal_release_locked_buffer( m ); | |
474 | dt3155_fbuffer[ m ]->empty_len = 0; | |
475 | ||
476 | for ( index = 0; index < dt3155_fbuffer[ m ]->nbuffers; index++ ) | |
477 | push_empty( index, m ); | |
478 | ||
479 | /* Make sure there is an active buffer there. */ | |
480 | dt3155_fbuffer[ m ]->active_buf = pop_empty( m ); | |
481 | ||
482 | dt3155_fbuffer[ m ]->even_happened = 0; | |
483 | dt3155_fbuffer[ m ]->even_stopped = 0; | |
484 | ||
485 | /* setup the ready queue */ | |
486 | dt3155_fbuffer[ m ]->ready_head = 0; | |
487 | dt3155_fbuffer[ m ]->ready_len = 0; | |
488 | ||
489 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
490 | local_irq_restore(flags); | |
491 | #else | |
492 | restore_flags( flags ); | |
493 | #endif | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | /***************************************************** | |
499 | * dt3155_get_ready_buffer() | |
500 | * m is minor # of device | |
501 | * | |
502 | * get_ready_buffer will grab the next chunk of data | |
503 | * if it is already there, otherwise it returns 0. | |
504 | * If the user has a buffer locked it will unlock | |
505 | * that buffer before returning the new one. | |
506 | *****************************************************/ | |
507 | inline int dt3155_get_ready_buffer( int m ) | |
508 | { | |
509 | int frame_index; | |
510 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
511 | unsigned long int flags; | |
512 | local_save_flags(flags); | |
513 | local_irq_disable(); | |
514 | #else | |
515 | int flags; | |
516 | save_flags( flags ); | |
517 | cli(); | |
518 | #endif | |
519 | ||
520 | #ifdef DEBUG_QUES_A | |
521 | printques( m ); | |
522 | #endif | |
523 | ||
524 | internal_release_locked_buffer( m ); | |
525 | ||
526 | if (is_ready_buf_empty( m )) | |
527 | frame_index = -1; | |
528 | else | |
529 | { | |
530 | frame_index = pop_ready( m ); | |
531 | dt3155_fbuffer[ m ]->locked_buf = frame_index; | |
532 | } | |
533 | ||
534 | #ifdef DEBUG_QUES_B | |
535 | printques( m ); | |
536 | #endif | |
537 | ||
538 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
539 | local_irq_restore(flags); | |
540 | #else | |
541 | restore_flags( flags ); | |
542 | #endif | |
543 | ||
544 | return frame_index; | |
545 | } |