]>
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> | |
5a0e3ad6 | 48 | #include <linux/gfp.h> |
aa337ef1 SS |
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 | *****************************************************/ | |
3a8954e8 | 223 | u32 adjust_4MB (u32 buf_addr, u32 bufsize) { |
aa337ef1 SS |
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 | *****************************************************/ | |
3a8954e8 HS |
238 | void allocate_buffers (u32 *buf_addr, u32* total_size_kbs, |
239 | u32 bufsize) | |
aa337ef1 SS |
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 | ||
3a8954e8 HS |
245 | u32 allocator_max; |
246 | u32 bufs_per_chunk = (FOUR_MB / bufsize); | |
247 | u32 filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk; | |
248 | u32 leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk; | |
aa337ef1 | 249 | |
3a8954e8 | 250 | u32 full_size = bufsize /* possibly unusable part of 1st chunk */ |
aa337ef1 SS |
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 | ||
3a8954e8 HS |
255 | u32 full_size_kbs = 1 + (full_size-1) / 1024; |
256 | u32 min_size_kbs = 2*ndevices*bufsize / 1024; | |
257 | u32 size_kbs; | |
aa337ef1 SS |
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; | |
3a8954e8 HS |
267 | printk("DT3155: We would like to get: %d KB\n", full_size_kbs); |
268 | printk("DT3155: ...but need at least: %d KB\n", min_size_kbs); | |
269 | printk("DT3155: ...the allocator has: %d KB\n", allocator_max); | |
aa337ef1 SS |
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) { | |
3a8954e8 | 273 | printk("DT3155: Managed to allocate: %d KB\n", size_kbs); |
aa337ef1 SS |
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 | *****************************************************/ | |
3a8954e8 | 301 | u32 dt3155_setup_buffers(u32 *allocatorAddr) |
aa337ef1 SS |
302 | |
303 | { | |
3a8954e8 HS |
304 | u32 index; |
305 | u32 rambuff_addr; /* start of allocation */ | |
306 | u32 rambuff_size; /* total size allocated to driver */ | |
307 | u32 rambuff_acm; /* accumlator, keep track of how much | |
aa337ef1 | 308 | is left after being split up*/ |
3a8954e8 HS |
309 | u32 rambuff_end; /* end of rambuff */ |
310 | u32 numbufs; /* number of useful buffers allocated (per device) */ | |
311 | u32 bufsize = DT3155_MAX_ROWS * DT3155_MAX_COLS; | |
aa337ef1 SS |
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 | { | |
5d392111 | 321 | u8 *ptr = (u8 *) dt3155_fbuffer[ m ]; |
aa337ef1 SS |
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); | |
3a8954e8 HS |
329 | printk("DT3155: mem info\n"); |
330 | printk(" - rambuf_addr = 0x%x \n", rambuff_addr); | |
331 | printk(" - length (kb) = %u \n", rambuff_size); | |
aa337ef1 SS |
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; | |
3a8954e8 | 353 | printk(" - numbufs = %u\n", numbufs); |
aa337ef1 SS |
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 | { | |
aa337ef1 SS |
438 | unsigned long int flags; |
439 | local_save_flags(flags); | |
440 | local_irq_disable(); | |
441 | internal_release_locked_buffer(m); | |
442 | local_irq_restore(flags); | |
aa337ef1 SS |
443 | } |
444 | ||
445 | ||
446 | /***************************************************** | |
447 | * dt3155_flush() | |
448 | * m is minor # of device | |
449 | * | |
450 | *****************************************************/ | |
451 | inline int dt3155_flush( int m ) | |
452 | { | |
453 | int index; | |
2141ec62 SH |
454 | unsigned long int flags; |
455 | local_save_flags(flags); | |
456 | local_irq_disable(); | |
aa337ef1 SS |
457 | |
458 | internal_release_locked_buffer( m ); | |
459 | dt3155_fbuffer[ m ]->empty_len = 0; | |
460 | ||
461 | for ( index = 0; index < dt3155_fbuffer[ m ]->nbuffers; index++ ) | |
462 | push_empty( index, m ); | |
463 | ||
464 | /* Make sure there is an active buffer there. */ | |
465 | dt3155_fbuffer[ m ]->active_buf = pop_empty( m ); | |
466 | ||
467 | dt3155_fbuffer[ m ]->even_happened = 0; | |
468 | dt3155_fbuffer[ m ]->even_stopped = 0; | |
469 | ||
470 | /* setup the ready queue */ | |
471 | dt3155_fbuffer[ m ]->ready_head = 0; | |
472 | dt3155_fbuffer[ m ]->ready_len = 0; | |
473 | ||
aa337ef1 | 474 | local_irq_restore(flags); |
aa337ef1 SS |
475 | |
476 | return 0; | |
477 | } | |
478 | ||
479 | /***************************************************** | |
480 | * dt3155_get_ready_buffer() | |
481 | * m is minor # of device | |
482 | * | |
483 | * get_ready_buffer will grab the next chunk of data | |
484 | * if it is already there, otherwise it returns 0. | |
485 | * If the user has a buffer locked it will unlock | |
486 | * that buffer before returning the new one. | |
487 | *****************************************************/ | |
488 | inline int dt3155_get_ready_buffer( int m ) | |
489 | { | |
490 | int frame_index; | |
2141ec62 SH |
491 | unsigned long int flags; |
492 | local_save_flags(flags); | |
493 | local_irq_disable(); | |
aa337ef1 SS |
494 | |
495 | #ifdef DEBUG_QUES_A | |
496 | printques( m ); | |
497 | #endif | |
498 | ||
499 | internal_release_locked_buffer( m ); | |
500 | ||
501 | if (is_ready_buf_empty( m )) | |
502 | frame_index = -1; | |
503 | else | |
504 | { | |
505 | frame_index = pop_ready( m ); | |
506 | dt3155_fbuffer[ m ]->locked_buf = frame_index; | |
507 | } | |
508 | ||
509 | #ifdef DEBUG_QUES_B | |
510 | printques( m ); | |
511 | #endif | |
512 | ||
aa337ef1 | 513 | local_irq_restore(flags); |
aa337ef1 SS |
514 | |
515 | return frame_index; | |
516 | } |