| /* |
| |
| Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, |
| Jason Lapenta, Scott Smedley, Greg Sharp |
| |
| This file is part of the DT3155 Device Driver. |
| |
| The DT3155 Device Driver is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| The DT3155 Device Driver is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with the DT3155 Device Driver; if not, write to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| MA 02111-1307 USA |
| |
| File: dt3155_isr.c |
| Purpose: Buffer management routines, and other routines for the ISR |
| (the actual isr is in dt3155_drv.c) |
| |
| -- Changes -- |
| |
| Date Programmer Description of changes made |
| ------------------------------------------------------------------- |
| 03-Jul-2000 JML n/a |
| 02-Apr-2002 SS Mods to make work with separate allocator |
| module; Merged John Roll's mods to make work with |
| multiple boards. |
| 10-Jul-2002 GCS Complete rewrite of setup_buffers to disallow |
| buffers which span a 4MB boundary. |
| 24-Jul-2002 SS GPL licence. |
| 30-Jul-2002 NJC Added support for buffer loop. |
| 31-Jul-2002 NJC Complete rewrite of buffer management |
| 02-Aug-2002 NJC Including slab.h instead of malloc.h (no warning). |
| Also, allocator_init() now returns allocator_max |
| so cleaned up allocate_buffers() accordingly. |
| 08-Aug-2005 SS port to 2.6 kernel. |
| |
| */ |
| |
| #include <asm/system.h> |
| #include <linux/gfp.h> |
| #include <linux/sched.h> |
| #include <linux/types.h> |
| |
| #include "dt3155.h" |
| #include "dt3155_drv.h" |
| #include "dt3155_io.h" |
| #include "dt3155_isr.h" |
| #include "allocator.h" |
| |
| #define FOUR_MB (0x0400000) /* Can't DMA accross a 4MB boundary!*/ |
| #define UPPER_10_BITS (0x3FF<<22) /* Can't DMA accross a 4MB boundary!*/ |
| |
| |
| /* Pointer into global structure for handling buffers */ |
| struct dt3155_fbuffer *dt3155_fbuffer[MAXBOARDS] = {NULL |
| #if MAXBOARDS == 2 |
| , NULL |
| #endif |
| }; |
| |
| /****************************************************************************** |
| * Simple array based que struct |
| * |
| * Some handy functions using the buffering structure. |
| *****************************************************************************/ |
| |
| |
| /*************************** |
| * are_empty_buffers |
| * m is minor # of device |
| ***************************/ |
| bool are_empty_buffers(int m) |
| { |
| return dt3155_fbuffer[m]->empty_len; |
| } |
| |
| /************************** |
| * push_empty |
| * m is minor # of device |
| * |
| * This is slightly confusing. The number empty_len is the literal # |
| * of empty buffers. After calling, empty_len-1 is the index into the |
| * empty buffer stack. So, if empty_len == 1, there is one empty buffer, |
| * given by dt3155_fbuffer[m]->empty_buffers[0]. |
| * empty_buffers should never fill up, though this is not checked. |
| **************************/ |
| void push_empty(int index, int m) |
| { |
| dt3155_fbuffer[m]->empty_buffers[dt3155_fbuffer[m]->empty_len] = index; |
| dt3155_fbuffer[m]->empty_len++; |
| } |
| |
| /************************** |
| * pop_empty(m) |
| * m is minor # of device |
| **************************/ |
| int pop_empty(int m) |
| { |
| dt3155_fbuffer[m]->empty_len--; |
| return dt3155_fbuffer[m]->empty_buffers[dt3155_fbuffer[m]->empty_len]; |
| } |
| |
| /************************* |
| * is_ready_buf_empty(m) |
| * m is minor # of device |
| *************************/ |
| bool is_ready_buf_empty(int m) |
| { |
| return ((dt3155_fbuffer[m]->ready_len) == 0); |
| } |
| |
| /************************* |
| * is_ready_buf_full(m) |
| * m is minor # of device |
| * this should *never* be true if there are any active, locked or empty |
| * buffers, since it corresponds to nbuffers ready buffers!! |
| * 7/31/02: total rewrite. --NJC |
| *************************/ |
| bool is_ready_buf_full(int m) |
| { |
| return dt3155_fbuffer[m]->ready_len == dt3155_fbuffer[m]->nbuffers; |
| } |
| |
| /***************************************************** |
| * push_ready(m, buffer) |
| * m is minor # of device |
| * |
| *****************************************************/ |
| void push_ready(int m, int index) |
| { |
| int head = dt3155_fbuffer[m]->ready_head; |
| |
| dt3155_fbuffer[m]->ready_que[head] = index; |
| dt3155_fbuffer[m]->ready_head = ((head + 1) % |
| (dt3155_fbuffer[m]->nbuffers)); |
| dt3155_fbuffer[m]->ready_len++; |
| |
| } |
| |
| /***************************************************** |
| * get_tail() |
| * m is minor # of device |
| * |
| * Simply comptutes the tail given the head and the length. |
| *****************************************************/ |
| static int get_tail(int m) |
| { |
| return (dt3155_fbuffer[m]->ready_head - |
| dt3155_fbuffer[m]->ready_len + |
| dt3155_fbuffer[m]->nbuffers)% |
| (dt3155_fbuffer[m]->nbuffers); |
| } |
| |
| |
| |
| /***************************************************** |
| * pop_ready() |
| * m is minor # of device |
| * |
| * This assumes that there is a ready buffer ready... should |
| * be checked (e.g. with is_ready_buf_empty() prior to call. |
| *****************************************************/ |
| int pop_ready(int m) |
| { |
| int tail; |
| tail = get_tail(m); |
| dt3155_fbuffer[m]->ready_len--; |
| return dt3155_fbuffer[m]->ready_que[tail]; |
| } |
| |
| |
| /***************************************************** |
| * printques |
| * m is minor # of device |
| *****************************************************/ |
| void printques(int m) |
| { |
| int head = dt3155_fbuffer[m]->ready_head; |
| int tail; |
| int num = dt3155_fbuffer[m]->nbuffers; |
| int frame_index; |
| int index; |
| |
| tail = get_tail(m); |
| |
| printk("\n R:"); |
| for (index = tail; index != head; index++, index = index % (num)) { |
| frame_index = dt3155_fbuffer[m]->ready_que[index]; |
| printk(" %d ", frame_index); |
| } |
| |
| printk("\n E:"); |
| for (index = 0; index < dt3155_fbuffer[m]->empty_len; index++) { |
| frame_index = dt3155_fbuffer[m]->empty_buffers[index]; |
| printk(" %d ", frame_index); |
| } |
| |
| frame_index = dt3155_fbuffer[m]->active_buf; |
| printk("\n A: %d", frame_index); |
| |
| frame_index = dt3155_fbuffer[m]->locked_buf; |
| printk("\n L: %d\n", frame_index); |
| |
| } |
| |
| /***************************************************** |
| * adjust_4MB |
| * |
| * If a buffer intersects the 4MB boundary, push |
| * the start address up to the beginning of the |
| * next 4MB chunk (assuming bufsize < 4MB). |
| *****************************************************/ |
| u32 adjust_4MB(u32 buf_addr, u32 bufsize) |
| { |
| if (((buf_addr+bufsize) & UPPER_10_BITS) != (buf_addr & UPPER_10_BITS)) |
| return (buf_addr+bufsize) & UPPER_10_BITS; |
| else |
| return buf_addr; |
| } |
| |
| |
| /***************************************************** |
| * allocate_buffers |
| * |
| * Try to allocate enough memory for all requested |
| * buffers. If there is not enough free space |
| * try for less memory. |
| *****************************************************/ |
| void allocate_buffers(u32 *buf_addr, u32* total_size_kbs, |
| u32 bufsize) |
| { |
| /* Compute the minimum amount of memory guaranteed to hold all |
| MAXBUFFERS such that no buffer crosses the 4MB boundary. |
| Store this value in the variable "full_size" */ |
| |
| u32 allocator_max; |
| u32 bufs_per_chunk = (FOUR_MB / bufsize); |
| u32 filled_chunks = (MAXBUFFERS-1) / bufs_per_chunk; |
| u32 leftover_bufs = MAXBUFFERS - filled_chunks * bufs_per_chunk; |
| |
| u32 full_size = bufsize /* possibly unusable part of 1st chunk */ |
| + filled_chunks * FOUR_MB /* max # of completely filled 4mb chunks */ |
| + leftover_bufs * bufsize; /* these buffs will be in a partly filled |
| chunk at beginning or end */ |
| |
| u32 full_size_kbs = 1 + (full_size-1) / 1024; |
| u32 min_size_kbs = 2*ndevices*bufsize / 1024; |
| u32 size_kbs; |
| |
| /* Now, try to allocate full_size. If this fails, keep trying for |
| less & less memory until it succeeds. */ |
| #ifndef STANDALONE_ALLOCATOR |
| /* initialize the allocator */ |
| allocator_init(&allocator_max); |
| #endif |
| size_kbs = full_size_kbs; |
| *buf_addr = 0; |
| printk("DT3155: We would like to get: %d KB\n", full_size_kbs); |
| printk("DT3155: ...but need at least: %d KB\n", min_size_kbs); |
| printk("DT3155: ...the allocator has: %d KB\n", allocator_max); |
| size_kbs = (full_size_kbs <= allocator_max ? full_size_kbs : allocator_max); |
| if (size_kbs > min_size_kbs) { |
| if ((*buf_addr = allocator_allocate_dma(size_kbs, GFP_KERNEL)) != 0) { |
| printk("DT3155: Managed to allocate: %d KB\n", size_kbs); |
| *total_size_kbs = size_kbs; |
| return; |
| } |
| } |
| /* If we got here, the allocation failed */ |
| printk("DT3155: Allocator failed!\n"); |
| *buf_addr = 0; |
| *total_size_kbs = 0; |
| return; |
| |
| } |
| |
| |
| /***************************************************** |
| * dt3155_setup_buffers |
| * |
| * setup_buffers just puts the buffering system into |
| * a consistent state before the start of interrupts |
| * |
| * JML : it looks like all the buffers need to be |
| * continuous. So I'm going to try and allocate one |
| * continuous buffer. |
| * |
| * GCS : Fix DMA problems when buffer spans |
| * 4MB boundary. Also, add error checking. This |
| * function will return -ENOMEM when not enough memory. |
| *****************************************************/ |
| u32 dt3155_setup_buffers(u32 *allocatorAddr) |
| |
| { |
| u32 index; |
| u32 rambuff_addr; /* start of allocation */ |
| u32 rambuff_size; /* total size allocated to driver */ |
| u32 rambuff_acm; /* accumlator, keep track of how much |
| is left after being split up*/ |
| u32 rambuff_end; /* end of rambuff */ |
| u32 numbufs; /* number of useful buffers allocated (per device) */ |
| u32 bufsize = DT3155_MAX_ROWS * DT3155_MAX_COLS; |
| int m; /* minor # of device, looped for all devs */ |
| |
| /* zero the fbuffer status and address structure */ |
| for (m = 0; m < ndevices; m++) { |
| dt3155_fbuffer[m] = &(dt3155_status[m].fbuffer); |
| |
| /* Make sure the buffering variables are consistent */ |
| { |
| u8 *ptr = (u8 *) dt3155_fbuffer[m]; |
| for (index = 0; index < sizeof(struct dt3155_fbuffer); index++) |
| *(ptr++) = 0; |
| } |
| } |
| |
| /* allocate a large contiguous chunk of RAM */ |
| allocate_buffers(&rambuff_addr, &rambuff_size, bufsize); |
| printk("DT3155: mem info\n"); |
| printk(" - rambuf_addr = 0x%x\n", rambuff_addr); |
| printk(" - length (kb) = %u\n", rambuff_size); |
| if (rambuff_addr == 0) { |
| printk(KERN_INFO |
| "DT3155: Error setup_buffers() allocator dma failed\n"); |
| return -ENOMEM; |
| } |
| *allocatorAddr = rambuff_addr; |
| rambuff_end = rambuff_addr + 1024 * rambuff_size; |
| |
| /* after allocation, we need to count how many useful buffers there |
| are so we can give an equal number to each device */ |
| rambuff_acm = rambuff_addr; |
| for (index = 0; index < MAXBUFFERS; index++) { |
| rambuff_acm = adjust_4MB(rambuff_acm, bufsize);/*avoid spanning 4MB bdry*/ |
| if (rambuff_acm + bufsize > rambuff_end) |
| break; |
| rambuff_acm += bufsize; |
| } |
| /* Following line is OK, will waste buffers if index |
| * not evenly divisible by ndevices -NJC*/ |
| numbufs = index / ndevices; |
| printk(" - numbufs = %u\n", numbufs); |
| if (numbufs < 2) { |
| printk(KERN_INFO |
| "DT3155: Error setup_buffers() couldn't allocate 2 bufs/board\n"); |
| return -ENOMEM; |
| } |
| |
| /* now that we have board memory we spit it up */ |
| /* between the boards and the buffers */ |
| rambuff_acm = rambuff_addr; |
| for (m = 0; m < ndevices; m++) { |
| rambuff_acm = adjust_4MB(rambuff_acm, bufsize); |
| |
| /* Save the start of this boards buffer space (for mmap). */ |
| dt3155_status[m].mem_addr = rambuff_acm; |
| |
| for (index = 0; index < numbufs; index++) { |
| rambuff_acm = adjust_4MB(rambuff_acm, bufsize); |
| if (rambuff_acm + bufsize > rambuff_end) { |
| /* Should never happen */ |
| printk("DT3155 PROGRAM ERROR (GCS)\n" |
| "Error distributing allocated buffers\n"); |
| return -ENOMEM; |
| } |
| |
| dt3155_fbuffer[m]->frame_info[index].addr = rambuff_acm; |
| push_empty(index, m); |
| /* printk(" - Buffer : %lx\n", |
| * dt3155_fbuffer[m]->frame_info[index].addr); |
| */ |
| dt3155_fbuffer[m]->nbuffers += 1; |
| rambuff_acm += bufsize; |
| } |
| |
| /* Make sure there is an active buffer there. */ |
| dt3155_fbuffer[m]->active_buf = pop_empty(m); |
| dt3155_fbuffer[m]->even_happened = 0; |
| dt3155_fbuffer[m]->even_stopped = 0; |
| |
| /* make sure there is no locked_buf JML 2/28/00 */ |
| dt3155_fbuffer[m]->locked_buf = -1; |
| |
| dt3155_status[m].mem_size = |
| rambuff_acm - dt3155_status[m].mem_addr; |
| |
| /* setup the ready queue */ |
| dt3155_fbuffer[m]->ready_head = 0; |
| dt3155_fbuffer[m]->ready_len = 0; |
| printk("Available buffers for device %d: %d\n", |
| m, dt3155_fbuffer[m]->nbuffers); |
| } |
| |
| return 1; |
| } |
| |
| /***************************************************** |
| * internal_release_locked_buffer |
| * |
| * The internal function for releasing a locked buffer. |
| * It assumes interrupts are turned off. |
| * |
| * m is minor number of device |
| *****************************************************/ |
| static void internal_release_locked_buffer(int m) |
| { |
| /* Pointer into global structure for handling buffers */ |
| if (dt3155_fbuffer[m]->locked_buf >= 0) { |
| push_empty(dt3155_fbuffer[m]->locked_buf, m); |
| dt3155_fbuffer[m]->locked_buf = -1; |
| } |
| } |
| |
| |
| /***************************************************** |
| * dt3155_release_locked_buffer() |
| * m is minor # of device |
| * |
| * The user function of the above. |
| * |
| *****************************************************/ |
| void dt3155_release_locked_buffer(int m) |
| { |
| unsigned long int flags; |
| local_save_flags(flags); |
| local_irq_disable(); |
| internal_release_locked_buffer(m); |
| local_irq_restore(flags); |
| } |
| |
| |
| /***************************************************** |
| * dt3155_flush() |
| * m is minor # of device |
| * |
| *****************************************************/ |
| int dt3155_flush(int m) |
| { |
| int index; |
| unsigned long int flags; |
| local_save_flags(flags); |
| local_irq_disable(); |
| |
| internal_release_locked_buffer(m); |
| dt3155_fbuffer[m]->empty_len = 0; |
| |
| for (index = 0; index < dt3155_fbuffer[m]->nbuffers; index++) |
| push_empty(index, m); |
| |
| /* Make sure there is an active buffer there. */ |
| dt3155_fbuffer[m]->active_buf = pop_empty(m); |
| |
| dt3155_fbuffer[m]->even_happened = 0; |
| dt3155_fbuffer[m]->even_stopped = 0; |
| |
| /* setup the ready queue */ |
| dt3155_fbuffer[m]->ready_head = 0; |
| dt3155_fbuffer[m]->ready_len = 0; |
| |
| local_irq_restore(flags); |
| |
| return 0; |
| } |
| |
| /***************************************************** |
| * dt3155_get_ready_buffer() |
| * m is minor # of device |
| * |
| * get_ready_buffer will grab the next chunk of data |
| * if it is already there, otherwise it returns 0. |
| * If the user has a buffer locked it will unlock |
| * that buffer before returning the new one. |
| *****************************************************/ |
| int dt3155_get_ready_buffer(int m) |
| { |
| int frame_index; |
| unsigned long int flags; |
| local_save_flags(flags); |
| local_irq_disable(); |
| |
| #ifdef DEBUG_QUES_A |
| printques(m); |
| #endif |
| |
| internal_release_locked_buffer(m); |
| |
| if (is_ready_buf_empty(m)) |
| frame_index = -1; |
| else { |
| frame_index = pop_ready(m); |
| dt3155_fbuffer[m]->locked_buf = frame_index; |
| } |
| |
| #ifdef DEBUG_QUES_B |
| printques(m); |
| #endif |
| |
| local_irq_restore(flags); |
| |
| return frame_index; |
| } |